Update app.py
Browse files
app.py
CHANGED
|
@@ -164,119 +164,138 @@ class DataCollector:
|
|
| 164 |
|
| 165 |
|
| 166 |
def create_3d_visualization(earthquake_data=None, weather_data=None):
|
| 167 |
-
"""Create an interactive 3D visualization
|
| 168 |
threejs_html = """
|
| 169 |
-
<div id="visualizationContainer" style="width: 100%; height: 600px;">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| 171 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
|
| 172 |
<script>
|
| 173 |
-
// Set up scene
|
| 174 |
const container = document.getElementById('visualizationContainer');
|
| 175 |
const scene = new THREE.Scene();
|
|
|
|
| 176 |
const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
|
| 177 |
-
const renderer = new THREE.WebGLRenderer();
|
| 178 |
renderer.setSize(container.clientWidth, container.clientHeight);
|
| 179 |
container.appendChild(renderer.domElement);
|
| 180 |
|
| 181 |
-
//
|
| 182 |
-
const
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
});
|
| 188 |
-
const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
|
| 189 |
-
|
| 190 |
-
// Add random terrain elevation
|
| 191 |
-
const vertices = terrainGeometry.attributes.position.array;
|
| 192 |
-
for (let i = 0; i < vertices.length; i += 3) {
|
| 193 |
-
vertices[i + 2] = Math.random() * 10;
|
| 194 |
-
}
|
| 195 |
-
terrainGeometry.computeVertexNormals();
|
| 196 |
-
scene.add(terrain);
|
| 197 |
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
|
|
|
|
|
|
| 204 |
|
| 205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
const cities = {
|
| 207 |
-
'Islamabad':
|
| 208 |
-
'Karachi':
|
| 209 |
-
'Lahore':
|
|
|
|
|
|
|
| 210 |
};
|
| 211 |
|
| 212 |
Object.entries(cities).forEach(([name, coords]) => {
|
| 213 |
-
const
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
const y = (coords.lat - 30) * 2;
|
| 220 |
-
marker.position.set(x, y, 5);
|
| 221 |
-
scene.add(marker);
|
| 222 |
-
});
|
| 223 |
|
| 224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
"""
|
| 226 |
-
|
| 227 |
if earthquake_data:
|
| 228 |
threejs_html += """
|
|
|
|
| 229 |
const earthquakes = """ + json.dumps(earthquake_data['features']) + """;
|
| 230 |
earthquakes.forEach(quake => {
|
| 231 |
const coords = quake.geometry.coordinates;
|
| 232 |
const magnitude = quake.properties.mag;
|
| 233 |
|
| 234 |
-
const
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
const y = (coords[1] - 30) * 2;
|
| 245 |
-
const z = 5 + magnitude;
|
| 246 |
-
quakeMesh.position.set(x, y, z);
|
| 247 |
-
|
| 248 |
-
scene.add(quakeMesh);
|
| 249 |
});
|
| 250 |
"""
|
| 251 |
|
| 252 |
threejs_html += """
|
| 253 |
-
//
|
| 254 |
-
|
| 255 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 256 |
camera.lookAt(scene.position);
|
| 257 |
|
| 258 |
-
//
|
| 259 |
const controls = {
|
| 260 |
-
|
| 261 |
-
|
| 262 |
};
|
| 263 |
|
| 264 |
-
const gui = new dat.GUI();
|
| 265 |
-
|
| 266 |
-
gui.add(controls, '
|
|
|
|
| 267 |
|
| 268 |
-
// Animation
|
| 269 |
function animate() {
|
| 270 |
requestAnimationFrame(animate);
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
// Update terrain elevation
|
| 274 |
-
const vertices = terrainGeometry.attributes.position.array;
|
| 275 |
-
for (let i = 0; i < vertices.length; i += 3) {
|
| 276 |
-
vertices[i + 2] *= controls.elevationScale;
|
| 277 |
}
|
| 278 |
-
|
| 279 |
-
|
| 280 |
renderer.render(scene, camera);
|
| 281 |
}
|
| 282 |
animate();
|
|
|
|
| 164 |
|
| 165 |
|
| 166 |
def create_3d_visualization(earthquake_data=None, weather_data=None):
|
| 167 |
+
"""Create an interactive 3D visualization of Pakistan"""
|
| 168 |
threejs_html = """
|
| 169 |
+
<div id="visualizationContainer" style="width: 100%; height: 600px;">
|
| 170 |
+
<div style="position: absolute; top: 10px; left: 10px; background: rgba(255,255,255,0.8); padding: 10px; border-radius: 5px;">
|
| 171 |
+
<h3 style="margin: 0;">Pakistan Terrain Map</h3>
|
| 172 |
+
<p style="margin: 5px 0;">🔴 Cities | 🟡 Earthquake Events</p>
|
| 173 |
+
</div>
|
| 174 |
+
</div>
|
| 175 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| 176 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
|
| 177 |
<script>
|
|
|
|
| 178 |
const container = document.getElementById('visualizationContainer');
|
| 179 |
const scene = new THREE.Scene();
|
| 180 |
+
scene.background = new THREE.Color(0xc6e6ff); // Light blue sky
|
| 181 |
const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
|
| 182 |
+
const renderer = new THREE.WebGLRenderer({ antialias: true });
|
| 183 |
renderer.setSize(container.clientWidth, container.clientHeight);
|
| 184 |
container.appendChild(renderer.domElement);
|
| 185 |
|
| 186 |
+
// Pakistan outline coordinates (simplified)
|
| 187 |
+
const pakistanOutline = [
|
| 188 |
+
[75.3, 35.5], [72.8, 36.5], [71.2, 34.7], [69.5, 34.0],
|
| 189 |
+
[66.8, 31.2], [63.2, 26.8], [61.5, 25.0], [63.5, 23.5],
|
| 190 |
+
[66.7, 24.5], [67.8, 24.8], [68.9, 27.0], [70.5, 28.5],
|
| 191 |
+
[72.0, 30.0], [74.0, 32.5], [75.3, 35.5]
|
| 192 |
+
];
|
| 193 |
+
|
| 194 |
+
// Create Pakistan terrain
|
| 195 |
+
const shape = new THREE.Shape();
|
| 196 |
+
pakistanOutline.forEach((point, index) => {
|
| 197 |
+
if (index === 0) shape.moveTo(point[0] - 67, point[1] - 30);
|
| 198 |
+
else shape.lineTo(point[0] - 67, point[1] - 30);
|
| 199 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
|
| 201 |
+
const geometry = new THREE.ExtrudeGeometry(shape, {
|
| 202 |
+
depth: 2,
|
| 203 |
+
bevelEnabled: true,
|
| 204 |
+
bevelSegments: 2,
|
| 205 |
+
steps: 2,
|
| 206 |
+
bevelSize: 0.3,
|
| 207 |
+
bevelThickness: 0.3
|
| 208 |
+
});
|
| 209 |
|
| 210 |
+
const material = new THREE.MeshPhongMaterial({
|
| 211 |
+
color: 0x4CAF50,
|
| 212 |
+
flatShading: true,
|
| 213 |
+
side: THREE.DoubleSide
|
| 214 |
+
});
|
| 215 |
+
|
| 216 |
+
const pakistan = new THREE.Mesh(geometry, material);
|
| 217 |
+
scene.add(pakistan);
|
| 218 |
+
|
| 219 |
+
// Add major cities
|
| 220 |
const cities = {
|
| 221 |
+
'Islamabad': [73.0931, 33.7294],
|
| 222 |
+
'Karachi': [67.0011, 24.8607],
|
| 223 |
+
'Lahore': [74.3587, 31.5204],
|
| 224 |
+
'Peshawar': [71.5249, 34.0151],
|
| 225 |
+
'Quetta': [66.9750, 30.1798]
|
| 226 |
};
|
| 227 |
|
| 228 |
Object.entries(cities).forEach(([name, coords]) => {
|
| 229 |
+
const cityMarker = new THREE.Mesh(
|
| 230 |
+
new THREE.SphereGeometry(0.3, 32, 32),
|
| 231 |
+
new THREE.MeshPhongMaterial({ color: 0xff0000 })
|
| 232 |
+
);
|
| 233 |
+
cityMarker.position.set(coords[0] - 67, coords[1] - 30, 2.5);
|
| 234 |
+
scene.add(cityMarker);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
|
| 236 |
+
// Add city label
|
| 237 |
+
const div = document.createElement('div');
|
| 238 |
+
div.className = 'label';
|
| 239 |
+
div.textContent = name;
|
| 240 |
+
div.style.color = 'black';
|
| 241 |
+
div.style.position = 'absolute';
|
| 242 |
+
div.style.padding = '2px';
|
| 243 |
+
div.style.backgroundColor = 'rgba(255,255,255,0.7)';
|
| 244 |
+
div.style.borderRadius = '3px';
|
| 245 |
+
container.appendChild(div);
|
| 246 |
+
});
|
| 247 |
"""
|
| 248 |
+
|
| 249 |
if earthquake_data:
|
| 250 |
threejs_html += """
|
| 251 |
+
// Add earthquake markers
|
| 252 |
const earthquakes = """ + json.dumps(earthquake_data['features']) + """;
|
| 253 |
earthquakes.forEach(quake => {
|
| 254 |
const coords = quake.geometry.coordinates;
|
| 255 |
const magnitude = quake.properties.mag;
|
| 256 |
|
| 257 |
+
const quakeMarker = new THREE.Mesh(
|
| 258 |
+
new THREE.SphereGeometry(magnitude * 0.2, 32, 32),
|
| 259 |
+
new THREE.MeshPhongMaterial({
|
| 260 |
+
color: 0xFFD700,
|
| 261 |
+
opacity: 0.8,
|
| 262 |
+
transparent: true
|
| 263 |
+
})
|
| 264 |
+
);
|
| 265 |
+
quakeMarker.position.set(coords[0] - 67, coords[1] - 30, 2.5);
|
| 266 |
+
scene.add(quakeMarker);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
});
|
| 268 |
"""
|
| 269 |
|
| 270 |
threejs_html += """
|
| 271 |
+
// Lighting
|
| 272 |
+
const ambientLight = new THREE.AmbientLight(0x404040, 1);
|
| 273 |
+
scene.add(ambientLight);
|
| 274 |
+
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
|
| 275 |
+
directionalLight.position.set(5, 5, 5);
|
| 276 |
+
scene.add(directionalLight);
|
| 277 |
+
|
| 278 |
+
// Camera
|
| 279 |
+
camera.position.set(0, 0, 20);
|
| 280 |
camera.lookAt(scene.position);
|
| 281 |
|
| 282 |
+
// Controls
|
| 283 |
const controls = {
|
| 284 |
+
rotation: true,
|
| 285 |
+
elevation: 1.0
|
| 286 |
};
|
| 287 |
|
| 288 |
+
const gui = new dat.GUI({ autoPlace: false });
|
| 289 |
+
container.appendChild(gui.domElement);
|
| 290 |
+
gui.add(controls, 'rotation').name('Auto Rotate');
|
| 291 |
+
gui.add(controls, 'elevation', 0.5, 2).name('Terrain Height');
|
| 292 |
|
|
|
|
| 293 |
function animate() {
|
| 294 |
requestAnimationFrame(animate);
|
| 295 |
+
if (controls.rotation) {
|
| 296 |
+
pakistan.rotation.y += 0.005;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
}
|
| 298 |
+
pakistan.scale.z = controls.elevation;
|
|
|
|
| 299 |
renderer.render(scene, camera);
|
| 300 |
}
|
| 301 |
animate();
|