Spaces:
Sleeping
Sleeping
AdityaAdaki commited on
Commit ·
af11331
1
Parent(s): b0b8a40
- static/js/main.js +159 -43
- static/js/polygon-draw.js +101 -61
- templates/index.html +54 -12
static/js/main.js
CHANGED
|
@@ -1,60 +1,176 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
return;
|
| 16 |
}
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
});
|
| 24 |
}
|
| 25 |
|
| 26 |
-
function
|
| 27 |
-
const
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
fetch('/capture_screenshot', {
|
| 31 |
method: 'POST',
|
| 32 |
headers: {
|
| 33 |
-
'Content-Type': 'application/json'
|
| 34 |
},
|
| 35 |
-
body: JSON.stringify(
|
| 36 |
-
width: map.getContainer().clientWidth,
|
| 37 |
-
height: map.getContainer().clientHeight,
|
| 38 |
-
mapState: {
|
| 39 |
-
center: center,
|
| 40 |
-
zoom: map.getZoom(),
|
| 41 |
-
bounds: {
|
| 42 |
-
north: bounds.getNorth(),
|
| 43 |
-
south: bounds.getSouth(),
|
| 44 |
-
east: bounds.getEast(),
|
| 45 |
-
west: bounds.getWest()
|
| 46 |
-
}
|
| 47 |
-
},
|
| 48 |
-
polygon: drawnPolygon
|
| 49 |
-
})
|
| 50 |
})
|
| 51 |
.then(response => response.json())
|
| 52 |
.then(data => {
|
| 53 |
-
if (data.
|
| 54 |
-
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
}
|
| 57 |
-
window.location.href = `/analyze?image=${data.cutout_path || data.screenshot_path}`;
|
| 58 |
})
|
| 59 |
.catch(error => {
|
| 60 |
console.error('Error:', error);
|
|
|
|
| 1 |
+
let polygonDraw = null;
|
| 2 |
+
let currentMap = null;
|
| 3 |
+
|
| 4 |
+
$(document).ready(function() {
|
| 5 |
+
// Location search form submission
|
| 6 |
+
$('#locationForm').on('submit', function(e) {
|
| 7 |
+
e.preventDefault();
|
| 8 |
+
|
| 9 |
+
$.ajax({
|
| 10 |
+
url: '/search_location',
|
| 11 |
+
method: 'POST',
|
| 12 |
+
data: {
|
| 13 |
+
location: $('#location').val()
|
| 14 |
+
},
|
| 15 |
+
success: function(response) {
|
| 16 |
+
$('#mapContainer').removeClass('d-none');
|
| 17 |
+
loadMap(response.lat, response.lon);
|
| 18 |
+
},
|
| 19 |
+
error: function(xhr) {
|
| 20 |
+
alert('Error: ' + (xhr.responseJSON ? xhr.responseJSON.error : 'Location not found'));
|
| 21 |
+
}
|
| 22 |
+
});
|
| 23 |
+
});
|
| 24 |
+
|
| 25 |
+
// File upload form submission
|
| 26 |
+
$('#uploadForm').on('submit', function(e) {
|
| 27 |
+
e.preventDefault();
|
| 28 |
+
|
| 29 |
+
const formData = new FormData();
|
| 30 |
+
const fileInput = $('#imageUpload')[0];
|
| 31 |
+
|
| 32 |
+
if (fileInput.files.length === 0) {
|
| 33 |
+
alert('Please select a file to upload');
|
| 34 |
return;
|
| 35 |
}
|
| 36 |
+
|
| 37 |
+
formData.append('file', fileInput.files[0]);
|
| 38 |
+
|
| 39 |
+
$.ajax({
|
| 40 |
+
url: '/upload',
|
| 41 |
+
method: 'POST',
|
| 42 |
+
data: formData,
|
| 43 |
+
processData: false,
|
| 44 |
+
contentType: false,
|
| 45 |
+
success: function(response) {
|
| 46 |
+
window.location.href = `/analyze?image=${encodeURIComponent(response.filepath)}`;
|
| 47 |
+
},
|
| 48 |
+
error: function(xhr) {
|
| 49 |
+
alert('Error uploading file: ' + (xhr.responseJSON ? xhr.responseJSON.error : 'Upload failed'));
|
| 50 |
+
}
|
| 51 |
+
});
|
| 52 |
+
});
|
| 53 |
+
|
| 54 |
+
// Screenshot capture button click
|
| 55 |
+
$('#captureBtn').on('click', function() {
|
| 56 |
+
if (!polygonDraw) {
|
| 57 |
+
// Initialize polygon drawing if not already initialized
|
| 58 |
+
const mapContainer = document.getElementById('map');
|
| 59 |
+
mapContainer.classList.add('drawing-mode');
|
| 60 |
+
polygonDraw = new PolygonDraw(mapContainer);
|
| 61 |
+
polygonDraw.startDrawing();
|
| 62 |
+
$(this).text('Complete Drawing (Press Enter)');
|
| 63 |
+
} else if (polygonDraw.isDrawing) {
|
| 64 |
+
const points = polygonDraw.completePolygon();
|
| 65 |
+
const mapContainer = document.getElementById('map');
|
| 66 |
+
mapContainer.classList.remove('drawing-mode');
|
| 67 |
+
if (points.length >= 3) {
|
| 68 |
+
captureWithPolygon(points);
|
| 69 |
+
}
|
| 70 |
+
$(this).text('Capture Screenshot');
|
| 71 |
+
} else {
|
| 72 |
+
const mapContainer = document.getElementById('map');
|
| 73 |
+
mapContainer.classList.add('drawing-mode');
|
| 74 |
+
polygonDraw.startDrawing();
|
| 75 |
+
$(this).text('Complete Drawing (Press Enter)');
|
| 76 |
+
}
|
| 77 |
+
});
|
| 78 |
+
|
| 79 |
+
// Analyze button click
|
| 80 |
+
$('#analyzeBtn').on('click', function() {
|
| 81 |
+
const screenshotPath = $('#capturedImage').attr('src');
|
| 82 |
+
if (screenshotPath) {
|
| 83 |
+
window.location.href = `/analyze?image=${encodeURIComponent(screenshotPath)}`;
|
| 84 |
+
}
|
| 85 |
+
});
|
| 86 |
+
|
| 87 |
+
// Add keyboard event listener for Enter key
|
| 88 |
+
document.addEventListener('keydown', function(event) {
|
| 89 |
+
if (event.key === 'Enter' && polygonDraw && polygonDraw.isDrawing) {
|
| 90 |
+
const points = polygonDraw.completePolygon();
|
| 91 |
+
const mapContainer = document.getElementById('map');
|
| 92 |
+
mapContainer.classList.remove('drawing-mode');
|
| 93 |
+
if (points.length >= 3) {
|
| 94 |
+
captureWithPolygon(points);
|
| 95 |
+
}
|
| 96 |
+
$('#captureBtn').text('Capture Screenshot');
|
| 97 |
+
}
|
| 98 |
+
});
|
| 99 |
+
});
|
| 100 |
+
|
| 101 |
+
function loadMap(lat, lon) {
|
| 102 |
+
const mapDiv = document.getElementById('map');
|
| 103 |
+
mapDiv.innerHTML = ''; // Clear any existing content
|
| 104 |
+
|
| 105 |
+
// Create a new map instance
|
| 106 |
+
currentMap = L.map('map', {
|
| 107 |
+
center: [lat, lon],
|
| 108 |
+
zoom: 20,
|
| 109 |
+
maxZoom: 18,
|
| 110 |
+
preferCanvas: true
|
| 111 |
+
});
|
| 112 |
+
|
| 113 |
+
// Add the satellite tile layer
|
| 114 |
+
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
| 115 |
+
attribution: 'Esri',
|
| 116 |
+
maxZoom: 20,
|
| 117 |
+
tileSize: 256
|
| 118 |
+
}).addTo(currentMap);
|
| 119 |
+
|
| 120 |
+
// Wait for the map to load completely
|
| 121 |
+
currentMap.whenReady(() => {
|
| 122 |
+
currentMap.invalidateSize();
|
| 123 |
+
currentMap.setView([lat, lon], 20);
|
| 124 |
});
|
| 125 |
}
|
| 126 |
|
| 127 |
+
function captureWithPolygon(points) {
|
| 128 |
+
const mapContainer = document.getElementById('map');
|
| 129 |
+
|
| 130 |
+
// Get the current map state
|
| 131 |
+
const center = currentMap.getCenter();
|
| 132 |
+
const bounds = currentMap.getBounds();
|
| 133 |
+
const zoom = currentMap.getZoom();
|
| 134 |
|
| 135 |
+
const data = {
|
| 136 |
+
width: mapContainer.offsetWidth,
|
| 137 |
+
height: mapContainer.offsetHeight,
|
| 138 |
+
polygon: points,
|
| 139 |
+
mapState: {
|
| 140 |
+
center: {
|
| 141 |
+
lat: center.lat,
|
| 142 |
+
lng: center.lng
|
| 143 |
+
},
|
| 144 |
+
bounds: {
|
| 145 |
+
north: bounds.getNorth(),
|
| 146 |
+
south: bounds.getSouth(),
|
| 147 |
+
east: bounds.getEast(),
|
| 148 |
+
west: bounds.getWest()
|
| 149 |
+
},
|
| 150 |
+
zoom: zoom
|
| 151 |
+
}
|
| 152 |
+
};
|
| 153 |
+
|
| 154 |
+
// Log the map state for debugging
|
| 155 |
+
console.log('Capturing map state:', data.mapState);
|
| 156 |
+
|
| 157 |
fetch('/capture_screenshot', {
|
| 158 |
method: 'POST',
|
| 159 |
headers: {
|
| 160 |
+
'Content-Type': 'application/json'
|
| 161 |
},
|
| 162 |
+
body: JSON.stringify(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
})
|
| 164 |
.then(response => response.json())
|
| 165 |
.then(data => {
|
| 166 |
+
if (data.success) {
|
| 167 |
+
$('#screenshotGallery').removeClass('d-none');
|
| 168 |
+
const imagePath = data.cutout_path || data.screenshot_path;
|
| 169 |
+
const imageUrl = imagePath + '?t=' + new Date().getTime();
|
| 170 |
+
$('#capturedImage').attr('src', imageUrl);
|
| 171 |
+
} else {
|
| 172 |
+
alert('Error capturing screenshot');
|
| 173 |
}
|
|
|
|
| 174 |
})
|
| 175 |
.catch(error => {
|
| 176 |
console.error('Error:', error);
|
static/js/polygon-draw.js
CHANGED
|
@@ -1,70 +1,110 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
}
|
| 15 |
-
|
| 16 |
-
resizeCanvas();
|
| 17 |
-
window.addEventListener('resize', resizeCanvas);
|
| 18 |
-
|
| 19 |
-
// Setup map click handler
|
| 20 |
-
map.on('click', function(e) {
|
| 21 |
-
if (!drawing) return;
|
| 22 |
-
addPoint(e);
|
| 23 |
-
});
|
| 24 |
-
});
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
}
|
| 38 |
-
drawing = false;
|
| 39 |
-
document.getElementById('map').style.cursor = '';
|
| 40 |
-
}
|
| 41 |
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
};
|
| 51 |
-
|
| 52 |
-
drawnPolygon.push(point);
|
| 53 |
-
|
| 54 |
-
// Draw point
|
| 55 |
-
ctx.fillStyle = 'red';
|
| 56 |
-
ctx.beginPath();
|
| 57 |
-
ctx.arc(point.x, point.y, 3, 0, 2 * Math.PI);
|
| 58 |
-
ctx.fill();
|
| 59 |
-
|
| 60 |
-
// Draw line
|
| 61 |
-
if (drawnPolygon.length > 1) {
|
| 62 |
-
const prev = drawnPolygon[drawnPolygon.length - 2];
|
| 63 |
-
ctx.beginPath();
|
| 64 |
-
ctx.moveTo(prev.x, prev.y);
|
| 65 |
-
ctx.lineTo(point.x, point.y);
|
| 66 |
-
ctx.strokeStyle = 'red';
|
| 67 |
-
ctx.lineWidth = 2;
|
| 68 |
-
ctx.stroke();
|
| 69 |
}
|
| 70 |
}
|
|
|
|
| 1 |
+
class PolygonDraw {
|
| 2 |
+
constructor(container) {
|
| 3 |
+
this.container = container;
|
| 4 |
+
this.points = [];
|
| 5 |
+
this.isDrawing = false;
|
| 6 |
+
this.svg = null;
|
| 7 |
+
this.polygon = null;
|
| 8 |
+
this.setupSVG();
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
setupSVG() {
|
| 12 |
+
// Create SVG overlay
|
| 13 |
+
this.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
| 14 |
+
this.svg.style.position = 'absolute';
|
| 15 |
+
this.svg.style.top = '0';
|
| 16 |
+
this.svg.style.left = '0';
|
| 17 |
+
this.svg.style.width = '100%';
|
| 18 |
+
this.svg.style.height = '100%';
|
| 19 |
+
this.svg.style.pointerEvents = 'all';
|
| 20 |
+
this.svg.style.zIndex = '1000';
|
| 21 |
+
|
| 22 |
+
// Create polygon element
|
| 23 |
+
this.polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
|
| 24 |
+
this.polygon.setAttribute('fill', 'rgba(255, 0, 0, 0.2)');
|
| 25 |
+
this.polygon.setAttribute('stroke', 'red');
|
| 26 |
+
this.polygon.setAttribute('stroke-width', '2');
|
| 27 |
+
this.svg.appendChild(this.polygon);
|
| 28 |
+
|
| 29 |
+
// Add SVG to container
|
| 30 |
+
this.container.appendChild(this.svg);
|
| 31 |
+
}
|
| 32 |
|
| 33 |
+
startDrawing() {
|
| 34 |
+
this.isDrawing = true;
|
| 35 |
+
this.points = [];
|
| 36 |
+
this.updatePolygon();
|
| 37 |
+
|
| 38 |
+
// Add click listener to container
|
| 39 |
+
this.container.style.cursor = 'crosshair';
|
| 40 |
+
this.clickHandler = this.handleClick.bind(this);
|
| 41 |
+
this.moveHandler = this.handleMouseMove.bind(this);
|
| 42 |
+
|
| 43 |
+
// Find and disable the iframe
|
| 44 |
+
const mapFrame = this.container.querySelector('iframe');
|
| 45 |
+
if (mapFrame) {
|
| 46 |
+
mapFrame.style.pointerEvents = 'none';
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
this.svg.addEventListener('click', this.clickHandler);
|
| 50 |
+
this.svg.addEventListener('mousemove', this.moveHandler);
|
| 51 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
+
handleClick(event) {
|
| 54 |
+
event.preventDefault();
|
| 55 |
+
event.stopPropagation();
|
| 56 |
+
|
| 57 |
+
const rect = this.container.getBoundingClientRect();
|
| 58 |
+
const x = event.clientX - rect.left;
|
| 59 |
+
const y = event.clientY - rect.top;
|
| 60 |
+
|
| 61 |
+
this.points.push({ x, y });
|
| 62 |
+
this.updatePolygon();
|
| 63 |
+
}
|
| 64 |
|
| 65 |
+
handleMouseMove(event) {
|
| 66 |
+
if (this.points.length > 0) {
|
| 67 |
+
event.preventDefault();
|
| 68 |
+
event.stopPropagation();
|
| 69 |
+
|
| 70 |
+
const rect = this.container.getBoundingClientRect();
|
| 71 |
+
const x = event.clientX - rect.left;
|
| 72 |
+
const y = event.clientY - rect.top;
|
| 73 |
+
|
| 74 |
+
this.updatePolygon([...this.points, { x, y }]);
|
| 75 |
+
}
|
| 76 |
}
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
+
updatePolygon(points = this.points) {
|
| 79 |
+
if (points.length > 0) {
|
| 80 |
+
const pointsString = points.map(p => `${p.x},${p.y}`).join(' ');
|
| 81 |
+
this.polygon.setAttribute('points', pointsString);
|
| 82 |
+
} else {
|
| 83 |
+
this.polygon.setAttribute('points', '');
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
completePolygon() {
|
| 88 |
+
this.isDrawing = false;
|
| 89 |
+
this.container.style.cursor = 'default';
|
| 90 |
+
this.svg.removeEventListener('click', this.clickHandler);
|
| 91 |
+
this.svg.removeEventListener('mousemove', this.moveHandler);
|
| 92 |
+
|
| 93 |
+
// Re-enable the iframe
|
| 94 |
+
const mapFrame = this.container.querySelector('iframe');
|
| 95 |
+
if (mapFrame) {
|
| 96 |
+
mapFrame.style.pointerEvents = 'auto';
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
const points = [...this.points];
|
| 100 |
+
this.points = [];
|
| 101 |
+
this.updatePolygon();
|
| 102 |
+
return points;
|
| 103 |
+
}
|
| 104 |
|
| 105 |
+
cleanup() {
|
| 106 |
+
if (this.svg && this.svg.parentNode) {
|
| 107 |
+
this.svg.parentNode.removeChild(this.svg);
|
| 108 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
}
|
| 110 |
}
|
templates/index.html
CHANGED
|
@@ -19,21 +19,63 @@
|
|
| 19 |
<button onclick="searchLocation()">Search</button>
|
| 20 |
</div>
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
</div>
|
| 29 |
-
|
| 30 |
-
<button onclick="captureScreenshot()">Capture Screenshot</button>
|
| 31 |
</div>
|
| 32 |
-
|
| 33 |
-
<div id="screenshot-result" class="results"></div>
|
| 34 |
</div>
|
| 35 |
|
| 36 |
-
<script src="/
|
| 37 |
-
<script src="/
|
|
|
|
|
|
|
|
|
|
| 38 |
</body>
|
| 39 |
</html>
|
|
|
|
| 19 |
<button onclick="searchLocation()">Search</button>
|
| 20 |
</div>
|
| 21 |
|
| 22 |
+
<div class="card mb-4">
|
| 23 |
+
<div class="card-body">
|
| 24 |
+
<h5 class="card-title">
|
| 25 |
+
<i class="fas fa-upload me-2"></i>Or Upload an Image
|
| 26 |
+
</h5>
|
| 27 |
+
<form id="uploadForm" class="mb-3">
|
| 28 |
+
<div class="form-group">
|
| 29 |
+
<input type="file"
|
| 30 |
+
class="form-control"
|
| 31 |
+
id="imageUpload"
|
| 32 |
+
accept=".jpg,.jpeg,.png,.tif,.tiff"
|
| 33 |
+
required>
|
| 34 |
+
</div>
|
| 35 |
+
<button type="submit" class="btn btn-primary mt-3">
|
| 36 |
+
<i class="fas fa-upload me-2"></i>Upload and Analyze
|
| 37 |
+
</button>
|
| 38 |
+
</form>
|
| 39 |
+
</div>
|
| 40 |
+
</div>
|
| 41 |
+
|
| 42 |
+
<div id="mapContainer" class="card mb-4 d-none">
|
| 43 |
+
<div class="card-body">
|
| 44 |
+
<div id="map"></div>
|
| 45 |
+
<div class="mt-4 d-flex justify-content-between align-items-center flex-wrap">
|
| 46 |
+
<button id="captureBtn" class="btn btn-success">
|
| 47 |
+
<i class="fas fa-camera me-2"></i>Capture Screenshot
|
| 48 |
+
</button>
|
| 49 |
+
<small class="text-muted">
|
| 50 |
+
<i class="fas fa-info-circle me-2"></i>Use map controls to adjust the view
|
| 51 |
+
</small>
|
| 52 |
+
</div>
|
| 53 |
+
</div>
|
| 54 |
+
</div>
|
| 55 |
+
|
| 56 |
+
<div id="screenshotGallery" class="card d-none">
|
| 57 |
+
<div class="card-body">
|
| 58 |
+
<h5 class="card-title">
|
| 59 |
+
<i class="fas fa-image me-2"></i>Captured Image
|
| 60 |
+
</h5>
|
| 61 |
+
<div id="screenshotContainer" class="text-center">
|
| 62 |
+
<img id="capturedImage" class="img-fluid" alt="Captured map">
|
| 63 |
+
<div class="mt-4">
|
| 64 |
+
<a id="analyzeBtn" class="btn btn-primary">
|
| 65 |
+
<i class="fas fa-chart-bar me-2"></i>Analyze Image
|
| 66 |
+
</a>
|
| 67 |
+
</div>
|
| 68 |
+
</div>
|
| 69 |
+
</div>
|
| 70 |
+
</div>
|
| 71 |
</div>
|
|
|
|
|
|
|
| 72 |
</div>
|
|
|
|
|
|
|
| 73 |
</div>
|
| 74 |
|
| 75 |
+
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
| 76 |
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
| 77 |
+
<script src="{{ url_for('static', filename='js/polygon-draw.js') }}"></script>
|
| 78 |
+
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
| 79 |
+
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
| 80 |
</body>
|
| 81 |
</html>
|