244 lines
8.1 KiB
Python
244 lines
8.1 KiB
Python
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,
|
|
}
|