Connection Com scene with its own elements
This commit is contained in:
commit
f12945e689
Binary file not shown.
@ -1,51 +1,195 @@
|
|||||||
|
{% load static %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>LDS City</title>
|
<title>LDS City</title>
|
||||||
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
|
<!-- A-Frame 1.7.0 & environment component -->
|
||||||
<script src="https://raw.githack.com/donmccurdy/aframe-extras/v6.0.0/dist/aframe-extras.min.js"></script>
|
<script src="https://aframe.io/releases/1.7.0/aframe.min.js"></script>
|
||||||
|
<script src="https://unpkg.com/aframe-environment-component/dist/aframe-environment-component.min.js"></script>
|
||||||
|
|
||||||
|
<!-- 1) Simple “look-at” component to face the camera -->
|
||||||
|
<script>
|
||||||
|
AFRAME.registerComponent('billboard', {
|
||||||
|
schema: {type: 'selector'},
|
||||||
|
tick: function () {
|
||||||
|
if (!this.data) return;
|
||||||
|
// Make this entity face the camera each frame
|
||||||
|
this.el.object3D.lookAt(this.data.object3D.position);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<a-scene>
|
<a-scene shadow="type: pcfsoft"
|
||||||
<!-- Camera and controls -->
|
environment="preset: forest; dressing: trees; groundColor: #777; skyType: gradient; dressingAmount: 20;">
|
||||||
<a-entity camera look-controls wasd-controls position="0 2 0"></a-entity>
|
|
||||||
|
|
||||||
<!-- Sky -->
|
<!-- Camera & Controls (give it an id for look-at) -->
|
||||||
{% load static %}
|
<a-entity id="mainCamera" camera look-controls wasd-controls position="0 2 0"></a-entity>
|
||||||
<a-sky id="sky" src="{% static 'textures/sky.jpg' %}" radius="150"></a-sky>
|
|
||||||
|
|
||||||
<!-- Ground plane -->
|
<!-- Optional: Transparent ground plane (comment out if you want only environment ground) -->
|
||||||
<a-plane position="0 -0.1 0" rotation="-90 0 0" width="200" height="200" material="src: url(https://cdn.aframe.io/a-painter/images/floor.jpg); repeat: 100 100;"></a-plane>
|
<a-plane position="0 -0.1 0" rotation="-90 0 0"
|
||||||
|
width="200" height="200"
|
||||||
|
color="#444" opacity="0.3">
|
||||||
|
</a-plane>
|
||||||
|
|
||||||
<!-- Buildings -->
|
<!-- Buildings -->
|
||||||
{% for building in city_data.buildings %}
|
{% for building in city_data.buildings %}
|
||||||
<a-entity id="{{ building.id }}" status="{{ building.status }}">
|
<a-entity id="{{ building.id }}" status="{{ building.status }}">
|
||||||
<a-text value="Status: {{ building.status }}" position="{{ building.position_x }} {{ building.height }} {{ building.position_z }}" scale="2 2 2"></a-text>
|
<!-- Building geometry -->
|
||||||
<a-box position="{{ building.position_x }} 1 {{ building.position_z }}" width="{{ building.width }}" height="{{ building.height }}" depth="{{ building.depth }}" rotation="0 0 0" color="{{ building.color }}"></a-box>
|
<a-box position="{{ building.position_x }} 1 {{ building.position_z }}"
|
||||||
|
width="{{ building.width }}" height="{{ building.height }}" depth="{{ building.depth }}"
|
||||||
|
color="{{ building.color }}">
|
||||||
|
</a-box>
|
||||||
|
|
||||||
|
<!-- Label entity: plane + text, billboarded to face camera -->
|
||||||
|
<a-entity position="{{ building.position_x }} 3 {{ building.position_z }}"
|
||||||
|
billboard="#mainCamera">
|
||||||
|
<!-- Semi-transparent background plane -->
|
||||||
|
<a-plane width="4" height="1.2" color="#000" opacity="0.5"></a-plane>
|
||||||
|
<!-- Text in front of plane -->
|
||||||
|
<a-text value="Status: {{ building.status }}"
|
||||||
|
width="4"
|
||||||
|
align="center"
|
||||||
|
color="#FFF"
|
||||||
|
position="0 0 0.01">
|
||||||
|
</a-text>
|
||||||
|
</a-entity>
|
||||||
</a-entity>
|
</a-entity>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<!-- Lamps -->
|
<!-- Lamps -->
|
||||||
{% for lamp in city_data.lamps %}
|
{% for lamp in city_data.lamps %}
|
||||||
<a-entity id="{{ lamp.id }}" status="{{ lamp.status }}" position="{{ lamp.position_x }} 1 {{ lamp.position_z }}">
|
<a-entity id="{{ lamp.id }}" status="{{ lamp.status }}" position="{{ lamp.position_x }} 1 {{ lamp.position_z }}">
|
||||||
<a-text value="Status: {{ lamp.status }}" position="0 {{ lamp.height }} 0" scale="2 2 2"></a-text>
|
<!-- Lamp geometry -->
|
||||||
<a-cone radius-bottom="0.1" radius-top="0.5" height="{{ lamp.height }}" color="{{ lamp.color }}"></a-cone>
|
<a-cone radius-bottom="0.1" radius-top="0.5"
|
||||||
<a-sphere radius="0.2" color="#FFFFFF" position="0 {{ lamp.height }} 0"></a-sphere>
|
height="{{ lamp.height }}" color="{{ lamp.color }}">
|
||||||
|
</a-cone>
|
||||||
|
<a-sphere radius="0.2" color="#FFFFFF"
|
||||||
|
position="0 {{ lamp.height }} 0">
|
||||||
|
</a-sphere>
|
||||||
|
|
||||||
|
<!-- Label: billboard to camera -->
|
||||||
|
<a-entity position="0 3 0" billboard="#mainCamera">
|
||||||
|
<a-plane width="4" height="1.2" color="#000" opacity="0.5"></a-plane>
|
||||||
|
<a-text value="Status: {{ lamp.status }}"
|
||||||
|
width="4"
|
||||||
|
align="center"
|
||||||
|
color="#FFF"
|
||||||
|
position="0 0 0.01">
|
||||||
|
</a-text>
|
||||||
|
</a-entity>
|
||||||
</a-entity>
|
</a-entity>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
<!-- Trees -->
|
<!-- Trees -->
|
||||||
{% for tree in city_data.trees %}
|
{% for tree in city_data.trees %}
|
||||||
<a-entity id="{{ tree.id }}" status="{{ tree.status }}" position="{{ tree.position_x }} 1 {{ tree.position_z }}">
|
<a-entity id="{{ tree.id }}" status="{{ tree.status }}" position="{{ tree.position_x }} 1 {{ tree.position_z }}">
|
||||||
<a-text value="Status: {{ tree.status }}" position="0 {{ tree.height }} 0" scale="2 2 2"></a-text>
|
<!-- Tree trunk & leaves -->
|
||||||
<a-cone radius-bottom="{{ tree.radius_bottom }}" radius-top="{{ tree.radius_top }}" height="{{ tree.height }}" color="{{ tree.color_trunk }}"></a-cone>
|
<a-cone radius-bottom="{{ tree.radius_bottom }}"
|
||||||
<a-sphere radius="{{ tree.radius_top }}" color="{{ tree.color_leaves }}" position="0 {{ tree.height }} 0"></a-sphere>
|
radius-top="{{ tree.radius_top }}"
|
||||||
|
height="{{ tree.height }}"
|
||||||
|
color="{{ tree.color_trunk }}">
|
||||||
|
</a-cone>
|
||||||
|
<a-sphere radius="{{ tree.radius_top }}"
|
||||||
|
color="{{ tree.color_leaves }}"
|
||||||
|
position="0 {{ tree.height }} 0">
|
||||||
|
</a-sphere>
|
||||||
|
|
||||||
|
<!-- Label: billboard to camera -->
|
||||||
|
<a-entity position="0 3 0" billboard="#mainCamera">
|
||||||
|
<a-plane width="4" height="1.2" color="#000" opacity="0.5"></a-plane>
|
||||||
|
<a-text value="Status: {{ tree.status }}"
|
||||||
|
width="4"
|
||||||
|
align="center"
|
||||||
|
color="#FFF"
|
||||||
|
position="0 0 0.01">
|
||||||
|
</a-text>
|
||||||
|
</a-entity>
|
||||||
</a-entity>
|
</a-entity>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Cell Towers -->
|
||||||
|
{% for tower in city_data.towers %}
|
||||||
|
<a-entity id="tower{{ tower.id }}"
|
||||||
|
position="{{ tower.position_x }} {{ tower.position_y }} {{ tower.position_z }}">
|
||||||
|
<!-- Base tower cylinder -->
|
||||||
|
<a-cylinder height="{{ tower.height }}" radius="1" color="{{ tower.color }}"></a-cylinder>
|
||||||
|
|
||||||
|
<!-- Animated signal ring near top -->
|
||||||
|
<a-ring color="#FF0000"
|
||||||
|
radius-inner="2"
|
||||||
|
radius-outer="2.5"
|
||||||
|
position="0 {{ tower.height|add:'1' }} 0"
|
||||||
|
rotation="-90 0 0"
|
||||||
|
animation="property: scale; to: 1.5 1.5 1.5; dir: alternate; dur: 1000; loop: true">
|
||||||
|
</a-ring>
|
||||||
|
|
||||||
|
<!-- Tower label: billboard to camera -->
|
||||||
|
<a-entity position="0 -5 0" billboard="#mainCamera">
|
||||||
|
<a-plane width="4" height="1.2" color="#000" opacity="0.5"></a-plane>
|
||||||
|
<a-text value="📡 Tower {{ tower.id }} - {{ tower.status }}"
|
||||||
|
width="4"
|
||||||
|
align="center"
|
||||||
|
color="#FFF"
|
||||||
|
position="0 0 0.01">
|
||||||
|
</a-text>
|
||||||
|
</a-entity>
|
||||||
|
</a-entity>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Fiber Paths (Cylinders) -->
|
||||||
|
{% for fiber in city_data.fiber_paths %}
|
||||||
|
<a-entity>
|
||||||
|
<a-cylinder position="{{ fiber.mid_x }} {{ fiber.mid_y }} {{ fiber.mid_z }}"
|
||||||
|
height="{{ fiber.length }}"
|
||||||
|
radius="0.1"
|
||||||
|
rotation="90 {{ fiber.angle }} 0"
|
||||||
|
color="{{ fiber.color }}">
|
||||||
|
</a-cylinder>
|
||||||
|
|
||||||
|
<!-- Fiber label: billboard to camera -->
|
||||||
|
<a-entity position="{{ fiber.start_x }} 3 {{ fiber.start_z }}" billboard="#mainCamera">
|
||||||
|
<a-plane width="4" height="1.2" color="#000" opacity="0.5"></a-plane>
|
||||||
|
<a-text value="🔗 Fiber Path {{ fiber.id }} - {{ fiber.status }}"
|
||||||
|
width="4"
|
||||||
|
align="center"
|
||||||
|
color="{{ fiber.color }}"
|
||||||
|
position="0 0 0.01">
|
||||||
|
</a-text>
|
||||||
|
</a-entity>
|
||||||
|
</a-entity>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Wi-Fi Hotspots -->
|
||||||
|
{% for wifi in city_data.wifi_hotspots %}
|
||||||
|
<a-entity id="wifi{{ wifi.id }}"
|
||||||
|
position="{{ wifi.position_x }} {{ wifi.position_y }} {{ wifi.position_z }}">
|
||||||
|
<!-- Hotspot sphere (animated) -->
|
||||||
|
<a-sphere radius="{{ wifi.radius }}" color="{{ wifi.color }}"
|
||||||
|
animation="property: scale; to: 1.5 1.5 1.5; dir: alternate; dur: 1500; loop: true">
|
||||||
|
</a-sphere>
|
||||||
|
|
||||||
|
<!-- Coverage area (fixed or dynamic) -->
|
||||||
|
<a-sphere radius="5"
|
||||||
|
color="#00FFFF"
|
||||||
|
opacity="0.2"
|
||||||
|
position="0 {{ wifi.radius }} 0">
|
||||||
|
</a-sphere>
|
||||||
|
|
||||||
|
<!-- Wi-Fi label: billboard to camera -->
|
||||||
|
<a-entity position="0 3 0" billboard="#mainCamera">
|
||||||
|
<a-plane width="4" height="1.2" color="#000" opacity="0.5"></a-plane>
|
||||||
|
<a-text value="📶 WiFi {{ wifi.id }} - {{ wifi.status }}"
|
||||||
|
width="4"
|
||||||
|
align="center"
|
||||||
|
color="#FFF"
|
||||||
|
position="0 0 0.01">
|
||||||
|
</a-text>
|
||||||
|
</a-entity>
|
||||||
|
</a-entity>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
</a-scene>
|
</a-scene>
|
||||||
<script src="{% static 'js/lds_city_vr.js' %}"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
126
views.py
126
views.py
@ -3,17 +3,35 @@ from django.http import Http404
|
|||||||
import random
|
import random
|
||||||
import math
|
import math
|
||||||
|
|
||||||
|
def get_environment_preset(lat, long):
|
||||||
|
"""
|
||||||
|
Determines the A-Frame environment preset based on latitude and longitude.
|
||||||
|
You can adjust the logic to suit your needs.
|
||||||
|
"""
|
||||||
|
# Example logic: adjust these thresholds as needed
|
||||||
|
if lat >= 60 or lat <= -60:
|
||||||
|
return 'snow' # Polar regions: snow environment
|
||||||
|
elif lat >= 30 or lat <= -30:
|
||||||
|
return 'forest' # Mid-latitudes: forest environment
|
||||||
|
elif long >= 100:
|
||||||
|
return 'goldmine' # Arbitrary example: for far east longitudes, a 'goldmine' preset
|
||||||
|
else:
|
||||||
|
return 'desert' # Default to desert for lower latitudes and moderate longitudes
|
||||||
|
|
||||||
def city_digital_twin(request, city_id, innovation_pct=None, technology_pct=None, science_pct=None):
|
def city_digital_twin(request, city_id, innovation_pct=None, technology_pct=None, science_pct=None):
|
||||||
try:
|
try:
|
||||||
if city_id == "random_city":
|
lat = float(request.GET.get('lat', 0))
|
||||||
|
long = float(request.GET.get('long', 0))
|
||||||
|
|
||||||
|
if city_id == "com_con":
|
||||||
|
city_data = generate_com_con_city_data(lat, long)
|
||||||
|
elif city_id == "random_city":
|
||||||
city_data = generate_random_city_data()
|
city_data = generate_random_city_data()
|
||||||
elif city_id == "dream":
|
elif city_id == "dream":
|
||||||
# Retrieve percentages from GET parameters if not in URL
|
|
||||||
innovation_pct = innovation_pct or request.GET.get('innovation', 0)
|
innovation_pct = innovation_pct or request.GET.get('innovation', 0)
|
||||||
technology_pct = technology_pct or request.GET.get('technology', 0)
|
technology_pct = technology_pct or request.GET.get('technology', 0)
|
||||||
science_pct = science_pct or request.GET.get('science', 0)
|
science_pct = science_pct or request.GET.get('science', 0)
|
||||||
|
|
||||||
# Convert to integers
|
|
||||||
innovation_pct = int(innovation_pct)
|
innovation_pct = int(innovation_pct)
|
||||||
technology_pct = int(technology_pct)
|
technology_pct = int(technology_pct)
|
||||||
science_pct = int(science_pct)
|
science_pct = int(science_pct)
|
||||||
@ -21,22 +39,22 @@ def city_digital_twin(request, city_id, innovation_pct=None, technology_pct=None
|
|||||||
city_data = generate_random_city_data(innovation_pct, technology_pct, science_pct)
|
city_data = generate_random_city_data(innovation_pct, technology_pct, science_pct)
|
||||||
else:
|
else:
|
||||||
city_data = get_city_data(city_id)
|
city_data = get_city_data(city_id)
|
||||||
except ValueError:
|
|
||||||
raise Http404("City data not found.")
|
|
||||||
|
|
||||||
if not city_data:
|
if not city_data:
|
||||||
city_data = get_example_data()
|
city_data = get_example_data()
|
||||||
|
|
||||||
context = {'city_data': city_data}
|
preset = get_environment_preset(lat, long)
|
||||||
return render(request, 'pxy_city_digital_twins/city_digital_twin.html', context)
|
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'city_data': city_data,
|
||||||
|
'environment_preset': preset,
|
||||||
|
'lat': lat,
|
||||||
|
'long': long,
|
||||||
|
}
|
||||||
|
return render(request, 'pxy_city_digital_twins/city_digital_twin.html', context)
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
raise Http404("Invalid data provided.")
|
||||||
|
|
||||||
if not city_data:
|
|
||||||
# Fallback to example data if no city data is found
|
|
||||||
city_data = get_example_data()
|
|
||||||
|
|
||||||
context = {'city_data': city_data}
|
|
||||||
return render(request, 'pxy_city_digital_twins/city_digital_twin.html', context)
|
|
||||||
|
|
||||||
def get_city_data(city_id):
|
def get_city_data(city_id):
|
||||||
# Implement fetching logic here
|
# Implement fetching logic here
|
||||||
@ -164,7 +182,6 @@ def triangular_layout(num_elements):
|
|||||||
return positions
|
return positions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def generate_random_city_data(innovation_pct=100, technology_pct=100, science_pct=100, max_position=100, radius=50):
|
def generate_random_city_data(innovation_pct=100, technology_pct=100, science_pct=100, max_position=100, radius=50):
|
||||||
num_buildings = random.randint(5, 35)
|
num_buildings = random.randint(5, 35)
|
||||||
num_lamps = random.randint(5, 100)
|
num_lamps = random.randint(5, 100)
|
||||||
@ -241,3 +258,80 @@ def generate_random_city_data(innovation_pct=100, technology_pct=100, science_pc
|
|||||||
'lamps': lamps,
|
'lamps': lamps,
|
||||||
'trees': trees,
|
'trees': trees,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_environment_by_lat(lat):
|
||||||
|
if lat > 60 or lat < -60:
|
||||||
|
return 'yeti'
|
||||||
|
elif 30 < lat < 60 or -30 > lat > -60:
|
||||||
|
return 'forest'
|
||||||
|
else:
|
||||||
|
return 'desert'
|
||||||
|
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
def generate_com_con_city_data(lat, long):
|
||||||
|
random.seed(f"{lat},{long}")
|
||||||
|
|
||||||
|
center_x = lat % 100
|
||||||
|
center_z = long % 100
|
||||||
|
|
||||||
|
grid_size = 5
|
||||||
|
spacing = 15 # Distance between objects
|
||||||
|
|
||||||
|
# Towers in a grid
|
||||||
|
towers = []
|
||||||
|
for i in range(grid_size):
|
||||||
|
for j in range(grid_size):
|
||||||
|
x = center_x + (i - grid_size // 2) * spacing
|
||||||
|
z = center_z + (j - grid_size // 2) * spacing
|
||||||
|
towers.append({
|
||||||
|
'id': len(towers) + 1,
|
||||||
|
'status': 'Active' if random.random() > 0.2 else 'Inactive',
|
||||||
|
'position_x': x,
|
||||||
|
'position_y': 0,
|
||||||
|
'position_z': z,
|
||||||
|
'height': random.randint(40, 60),
|
||||||
|
'range': random.randint(500, 1000),
|
||||||
|
'color': '#ff4500'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Fiber paths connect neighboring towers
|
||||||
|
fiber_paths = []
|
||||||
|
for i in range(len(towers) - 1):
|
||||||
|
fiber_paths.append({
|
||||||
|
'id': i + 1,
|
||||||
|
'start_x': towers[i]['position_x'],
|
||||||
|
'start_z': towers[i]['position_z'],
|
||||||
|
'end_x': towers[i + 1]['position_x'],
|
||||||
|
'end_z': towers[i + 1]['position_z'],
|
||||||
|
'mid_x': (towers[i]['position_x'] + towers[i + 1]['position_x']) / 2,
|
||||||
|
'mid_y': 0.1,
|
||||||
|
'mid_z': (towers[i]['position_z'] + towers[i + 1]['position_z']) / 2,
|
||||||
|
'length': ((towers[i + 1]['position_x'] - towers[i]['position_x'])**2 + (towers[i + 1]['position_z'] - towers[i]['position_z'])**2)**0.5,
|
||||||
|
'angle': random.uniform(0, 360),
|
||||||
|
'status': 'Connected' if random.random() > 0.1 else 'Broken',
|
||||||
|
'color': '#4682b4'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Wi-Fi Hotspots scattered nearby but within grid bounds
|
||||||
|
wifi_hotspots = []
|
||||||
|
for i in range(10):
|
||||||
|
x = center_x + random.uniform(-spacing * grid_size / 2, spacing * grid_size / 2)
|
||||||
|
z = center_z + random.uniform(-spacing * grid_size / 2, spacing * grid_size / 2)
|
||||||
|
wifi_hotspots.append({
|
||||||
|
'id': i + 1,
|
||||||
|
'position_x': x,
|
||||||
|
'position_y': 1.5,
|
||||||
|
'position_z': z,
|
||||||
|
'status': 'Online' if random.random() > 0.2 else 'Offline',
|
||||||
|
'radius': random.randint(1, 3),
|
||||||
|
'color': '#32cd32'
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
'towers': towers,
|
||||||
|
'fiber_paths': fiber_paths,
|
||||||
|
'wifi_hotspots': wifi_hotspots
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user