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/[email protected]/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/[email protected]/dist/leaflet.js"></script>
|
80 |
</body>
|
81 |
</html>
|