Initial commit from local machine

This commit is contained in:
Ekaropolus 2025-03-01 19:40:14 -06:00
commit 3baadaa817
16 changed files with 319 additions and 0 deletions

0
__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
admin.py Normal file
View File

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
apps.py Normal file
View 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
View File

Binary file not shown.

3
models.py Normal file
View File

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View 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
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
urls.py Normal file
View 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
View 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,
}