Initial commit from local machine
This commit is contained in:
commit
3baadaa817
0
__init__.py
Normal file
0
__init__.py
Normal file
BIN
__pycache__/__init__.cpython-310.pyc
Normal file
BIN
__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/admin.cpython-310.pyc
Normal file
BIN
__pycache__/admin.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/apps.cpython-310.pyc
Normal file
BIN
__pycache__/apps.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/models.cpython-310.pyc
Normal file
BIN
__pycache__/models.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/urls.cpython-310.pyc
Normal file
BIN
__pycache__/urls.cpython-310.pyc
Normal file
Binary file not shown.
BIN
__pycache__/views.cpython-310.pyc
Normal file
BIN
__pycache__/views.cpython-310.pyc
Normal file
Binary file not shown.
3
admin.py
Normal file
3
admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
apps.py
Normal file
6
apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PxyCityDigitalTwinsConfig(AppConfig):
|
||||||
|
default_auto_field = "django.db.models.BigAutoField"
|
||||||
|
name = "pxy_city_digital_twins"
|
0
migrations/__init__.py
Normal file
0
migrations/__init__.py
Normal file
BIN
migrations/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
migrations/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
3
models.py
Normal file
3
models.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
51
templates/pxy_city_digital_twins/city_digital_twin.html
Normal file
51
templates/pxy_city_digital_twins/city_digital_twin.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>LDS City</title>
|
||||||
|
<script src="https://aframe.io/releases/1.4.0/aframe.min.js"></script>
|
||||||
|
<script src="https://raw.githack.com/donmccurdy/aframe-extras/v6.0.0/dist/aframe-extras.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a-scene>
|
||||||
|
<!-- Camera and controls -->
|
||||||
|
<a-entity camera look-controls wasd-controls position="0 2 0"></a-entity>
|
||||||
|
|
||||||
|
<!-- Sky -->
|
||||||
|
{% load static %}
|
||||||
|
<a-sky id="sky" src="{% static 'textures/sky.jpg' %}" radius="150"></a-sky>
|
||||||
|
|
||||||
|
<!-- Ground plane -->
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<!-- Buildings -->
|
||||||
|
{% for building in city_data.buildings %}
|
||||||
|
<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>
|
||||||
|
<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-entity>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Lamps -->
|
||||||
|
{% for lamp in city_data.lamps %}
|
||||||
|
<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>
|
||||||
|
<a-cone radius-bottom="0.1" radius-top="0.5" height="{{ lamp.height }}" color="{{ lamp.color }}"></a-cone>
|
||||||
|
<a-sphere radius="0.2" color="#FFFFFF" position="0 {{ lamp.height }} 0"></a-sphere>
|
||||||
|
</a-entity>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<!-- Trees -->
|
||||||
|
{% for tree in city_data.trees %}
|
||||||
|
<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>
|
||||||
|
<a-cone radius-bottom="{{ tree.radius_bottom }}" 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>
|
||||||
|
</a-entity>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
|
||||||
|
</a-scene>
|
||||||
|
<script src="{% static 'js/lds_city_vr.js' %}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
3
tests.py
Normal file
3
tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
10
urls.py
Normal file
10
urls.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
# Pattern to accept UUIDs
|
||||||
|
path('city/digital/twin/<uuid:city_id>/', views.city_digital_twin, name='city_digital_twin_uuid'),
|
||||||
|
|
||||||
|
# Pattern to accept string words
|
||||||
|
path('city/digital/twin/<str:city_id>/', views.city_digital_twin, name='city_digital_twin_str'),
|
||||||
|
]
|
243
views.py
Normal file
243
views.py
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
from django.http import Http404
|
||||||
|
import random
|
||||||
|
import math
|
||||||
|
|
||||||
|
def city_digital_twin(request, city_id, innovation_pct=None, technology_pct=None, science_pct=None):
|
||||||
|
try:
|
||||||
|
if city_id == "random_city":
|
||||||
|
city_data = generate_random_city_data()
|
||||||
|
elif city_id == "dream":
|
||||||
|
# Retrieve percentages from GET parameters if not in URL
|
||||||
|
innovation_pct = innovation_pct or request.GET.get('innovation', 0)
|
||||||
|
technology_pct = technology_pct or request.GET.get('technology', 0)
|
||||||
|
science_pct = science_pct or request.GET.get('science', 0)
|
||||||
|
|
||||||
|
# Convert to integers
|
||||||
|
innovation_pct = int(innovation_pct)
|
||||||
|
technology_pct = int(technology_pct)
|
||||||
|
science_pct = int(science_pct)
|
||||||
|
|
||||||
|
city_data = generate_random_city_data(innovation_pct, technology_pct, science_pct)
|
||||||
|
else:
|
||||||
|
city_data = get_city_data(city_id)
|
||||||
|
except ValueError:
|
||||||
|
raise Http404("City data not found.")
|
||||||
|
|
||||||
|
if not city_data:
|
||||||
|
city_data = get_example_data()
|
||||||
|
|
||||||
|
context = {'city_data': city_data}
|
||||||
|
return render(request, 'pxy_city_digital_twins/city_digital_twin.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
# Implement fetching logic here
|
||||||
|
# This is a mock function to demonstrate fetching logic
|
||||||
|
if str(city_id) == "1" or str(city_id) == "123e4567-e89b-12d3-a456-426614174000":
|
||||||
|
return {
|
||||||
|
# Real data retrieval logic goes here
|
||||||
|
}
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_example_data():
|
||||||
|
return {
|
||||||
|
'buildings': [
|
||||||
|
{
|
||||||
|
'id': 1,
|
||||||
|
'status': 'Occupied',
|
||||||
|
'position_x': 0,
|
||||||
|
'height': 10,
|
||||||
|
'position_z': 0,
|
||||||
|
'width': 5,
|
||||||
|
'depth': 5,
|
||||||
|
'color': '#8a2be2',
|
||||||
|
'file': '', # No file for a simple box representation
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 2,
|
||||||
|
'status': 'Vacant',
|
||||||
|
'position_x': 10,
|
||||||
|
'height': 15,
|
||||||
|
'position_z': 10,
|
||||||
|
'width': 7,
|
||||||
|
'depth': 7,
|
||||||
|
'color': '#5f9ea0',
|
||||||
|
'file': '', # No file for a simple box representation
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'lamps': [
|
||||||
|
{
|
||||||
|
'id': 1,
|
||||||
|
'status': 'Functional',
|
||||||
|
'position_x': 3,
|
||||||
|
'position_z': 3,
|
||||||
|
'height': 4,
|
||||||
|
'color': '#ffff00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 2,
|
||||||
|
'status': 'Broken',
|
||||||
|
'position_x': 8,
|
||||||
|
'position_z': 8,
|
||||||
|
'height': 4,
|
||||||
|
'color': '#ff0000',
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'trees': [
|
||||||
|
{
|
||||||
|
'id': 1,
|
||||||
|
'status': 'Healthy',
|
||||||
|
'position_x': 5,
|
||||||
|
'position_z': 5,
|
||||||
|
'height': 6,
|
||||||
|
'radius_bottom': 0.2,
|
||||||
|
'radius_top': 1,
|
||||||
|
'color_trunk': '#8b4513',
|
||||||
|
'color_leaves': '#228b22',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'id': 2,
|
||||||
|
'status': 'Diseased',
|
||||||
|
'position_x': 15,
|
||||||
|
'position_z': 15,
|
||||||
|
'height': 6,
|
||||||
|
'radius_bottom': 0.2,
|
||||||
|
'radius_top': 1,
|
||||||
|
'color_trunk': '#a0522d',
|
||||||
|
'color_leaves': '#6b8e23',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def rectangular_layout(num_elements, max_dimension):
|
||||||
|
grid_size = int(math.sqrt(num_elements))
|
||||||
|
spacing = max_dimension // grid_size
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'position_x': (i % grid_size) * spacing,
|
||||||
|
'position_z': (i // grid_size) * spacing
|
||||||
|
}
|
||||||
|
for i in range(num_elements)
|
||||||
|
]
|
||||||
|
|
||||||
|
def circular_layout(num_elements, radius):
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'position_x': radius * math.cos(2 * math.pi * i / num_elements),
|
||||||
|
'position_z': radius * math.sin(2 * math.pi * i / num_elements)
|
||||||
|
}
|
||||||
|
for i in range(num_elements)
|
||||||
|
]
|
||||||
|
|
||||||
|
def diagonal_layout(num_elements, max_position):
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
'position_x': i * max_position // num_elements,
|
||||||
|
'position_z': i * max_position // num_elements
|
||||||
|
}
|
||||||
|
for i in range(num_elements)
|
||||||
|
]
|
||||||
|
|
||||||
|
def triangular_layout(num_elements):
|
||||||
|
positions = []
|
||||||
|
row_length = 1
|
||||||
|
while num_elements > 0:
|
||||||
|
for i in range(row_length):
|
||||||
|
if num_elements <= 0:
|
||||||
|
break
|
||||||
|
positions.append({
|
||||||
|
'position_x': i * 10 - (row_length - 1) * 5, # Spread out each row symmetrically
|
||||||
|
'position_z': row_length * 10
|
||||||
|
})
|
||||||
|
num_elements -= 1
|
||||||
|
row_length += 1
|
||||||
|
return positions
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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_lamps = random.randint(5, 100)
|
||||||
|
num_trees = random.randint(5, 55)
|
||||||
|
|
||||||
|
# Buildings layout distribution
|
||||||
|
num_rectangular_buildings = int(num_buildings * innovation_pct / 100)
|
||||||
|
num_circular_buildings = (num_buildings - num_rectangular_buildings) // 2
|
||||||
|
num_triangular_buildings = num_buildings - num_rectangular_buildings - num_circular_buildings
|
||||||
|
|
||||||
|
building_positions = rectangular_layout(num_rectangular_buildings, max_position) + \
|
||||||
|
circular_layout(num_circular_buildings, radius) + \
|
||||||
|
triangular_layout(num_triangular_buildings)
|
||||||
|
|
||||||
|
# Lamps layout distribution
|
||||||
|
num_triangular_lamps = int(num_lamps * technology_pct / 100)
|
||||||
|
num_circular_lamps = (num_lamps - num_triangular_lamps) // 2
|
||||||
|
num_diagonal_lamps = num_lamps - num_triangular_lamps - num_circular_lamps
|
||||||
|
|
||||||
|
lamp_positions = triangular_layout(num_triangular_lamps) + \
|
||||||
|
circular_layout(num_circular_lamps, radius) + \
|
||||||
|
diagonal_layout(num_diagonal_lamps, max_position)
|
||||||
|
|
||||||
|
# Trees layout distribution
|
||||||
|
num_circular_trees = int(num_trees * science_pct / 100)
|
||||||
|
num_triangular_trees = (num_trees - num_circular_trees) // 2
|
||||||
|
num_diagonal_trees = num_trees - num_circular_trees - num_triangular_trees
|
||||||
|
|
||||||
|
tree_positions = circular_layout(num_circular_trees, radius) + \
|
||||||
|
triangular_layout(num_triangular_trees) + \
|
||||||
|
diagonal_layout(num_diagonal_trees, max_position)
|
||||||
|
|
||||||
|
buildings = [
|
||||||
|
{
|
||||||
|
'id': i + 1,
|
||||||
|
'status': random.choice(['Occupied', 'Vacant', 'Under Construction']),
|
||||||
|
'position_x': pos['position_x'],
|
||||||
|
'position_z': pos['position_z'],
|
||||||
|
'height': random.randint(10, 50),
|
||||||
|
'width': random.randint(5, 20),
|
||||||
|
'depth': random.randint(5, 20),
|
||||||
|
'color': random.choice(['#8a2be2', '#5f9ea0', '#ff6347', '#4682b4']),
|
||||||
|
'file': ''
|
||||||
|
} for i, pos in enumerate(building_positions)
|
||||||
|
]
|
||||||
|
|
||||||
|
lamps = [
|
||||||
|
{
|
||||||
|
'id': i + 1,
|
||||||
|
'status': random.choice(['Functional', 'Non-functional']),
|
||||||
|
'position_x': pos['position_x'],
|
||||||
|
'position_z': pos['position_z'],
|
||||||
|
'height': random.randint(3, 10),
|
||||||
|
'color': random.choice(['#ffff00', '#ff0000', '#00ff00']),
|
||||||
|
} for i, pos in enumerate(lamp_positions)
|
||||||
|
]
|
||||||
|
|
||||||
|
trees = [
|
||||||
|
{
|
||||||
|
'id': i + 1,
|
||||||
|
'status': random.choice(['Healthy', 'Diseased', 'Wilting']),
|
||||||
|
'position_x': pos['position_x'],
|
||||||
|
'position_z': pos['position_z'],
|
||||||
|
'height': random.randint(5, 30),
|
||||||
|
'radius_bottom': random.uniform(0.1, 0.5),
|
||||||
|
'radius_top': random.uniform(0.5, 2.0),
|
||||||
|
'color_trunk': '#8b4513',
|
||||||
|
'color_leaves': random.choice(['#228b22', '#90ee90', '#8b4513']),
|
||||||
|
} for i, pos in enumerate(tree_positions)
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
'buildings': buildings,
|
||||||
|
'lamps': lamps,
|
||||||
|
'trees': trees,
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user