Ekaropolus 60df25b39a
All checks were successful
continuous-integration/drone/push Build is passing
Waste Urban Digital Twin
2025-05-21 12:28:12 -06:00

371 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from django.shortcuts import render, get_object_or_404
from django.http import Http404
import random
import math
from .services.presets import get_environment_preset
import networkx as nx
from .services.layouts import (
rectangular_layout,
circular_layout,
diagonal_layout,
triangular_layout,
)
from .services.random_city import generate_random_city_data
from .services.com_con_city import generate_com_con_city_data
from .services.osm_city import generate_osm_city_data, generate_osm_road_midpoints_only
def city_digital_twin(request, city_id, innovation_pct=None, technology_pct=None, science_pct=None):
try:
lat = float(request.GET.get('lat', 0))
long = float(request.GET.get('long', 0))
scale = float(request.GET.get('scale', 1.0)) # default to 1.0 (normal scale)
if city_id == "osm_city":
city_data = generate_osm_city_data(lat, long,scale=scale)
elif city_id == "com_con":
city_data = generate_com_con_city_data(lat, long)
elif city_id == "random_city":
city_data = generate_random_city_data()
elif city_id == "dream":
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)
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)
if not city_data:
city_data = get_example_data()
preset = get_environment_preset(lat, long)
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.")
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 city_augmented_digital_twin(request, city_id):
try:
lat = float(request.GET.get('lat', 0))
long = float(request.GET.get('long', 0))
scale = float(request.GET.get('scale', 1.0)) # default to 1.0
if city_id == "osm_city":
city_data = generate_osm_road_midpoints_only(lat, long, scale=scale)
elif city_id == "random_city":
city_data = generate_random_city_data()
elif city_id == "gauge_ahead":
# No city data needed, just render the special AR scene
return render(request, 'pxy_city_digital_twins/spawn_gauge_ahead.html')
else:
raise Http404("Unsupported city_id for AR view")
preset = get_environment_preset(lat, long)
context = {
'city_data': city_data,
'environment_preset': preset,
'lat': lat,
'long': long,
}
return render(request, 'pxy_city_digital_twins/city_augmented_digital_twin.html', context)
except (ValueError, TypeError):
raise Http404("Invalid parameters provided.")
from django.shortcuts import render
from django.http import Http404
from .services.waste_routes import get_dispatch_data_for
import json
def waste_route_debug(request, subdivision, vehicle):
# 1) load all routes for this subdivision
dispatch_data = get_dispatch_data_for(subdivision)
if not dispatch_data:
return render(request, 'pxy_city_digital_twins/waste_route_default.html', {
'subdivision': subdivision
})
# 2) pick your route by the required `vehicle` path-param
try:
selected = next(r for r in dispatch_data if r['route_id'] == vehicle)
except StopIteration:
return render(request, 'pxy_city_digital_twins/waste_route_not_found.html', {
'subdivision': subdivision
})
# 3) derive a “center” latitude/longitude from that route:
coords = selected.get('coords', [])
if coords:
avg_lon = sum(pt[0] for pt in coords) / len(coords)
avg_lat = sum(pt[1] for pt in coords) / len(coords)
else:
steps = selected.get('steps', [])
avg_lon = sum(s['position'][0] for s in steps) / len(steps)
avg_lat = sum(s['position'][1] for s in steps) / len(steps)
# 4) generate your OSMbased city around that center
city_data = generate_osm_city_data(avg_lat, avg_lon)
# 5) sanitize building heights (replace NaN or missing with default 10.0)
default_height = 10.0
for b in city_data.get('buildings', []):
h = b.get('height')
if not isinstance(h, (int, float)) or (isinstance(h, float) and math.isnan(h)):
b['height'] = default_height
# 6) render the VR template
return render(request, 'pxy_city_digital_twins/waste_route_debug.html', {
'subdivision': subdivision,
'vehicle': vehicle,
'selected_route_json': json.dumps(selected),
'city_data': city_data,
})
import math
import json
from django.shortcuts import render
from pyproj import Transformer
from .services.osm_city import generate_osm_city_data
from .services.waste_routes import get_dispatch_data_for
def waste_route(request, subdivision, vehicle):
"""
URL: /waste/<subdivision>/<vehicle>/
Renders a single vehicle's wastecollection route in VR,
overlaid on an OSMgenerated city around the route center.
"""
# 1) load all routes for this subdivision
dispatch_data = get_dispatch_data_for(subdivision)
if not dispatch_data:
return render(request, 'pxy_city_digital_twins/waste_route_default.html', {
'subdivision': subdivision
})
# 2) pick your route by the required `vehicle` path-param
try:
selected = next(r for r in dispatch_data if r['route_id'] == vehicle)
except StopIteration:
return render(request, 'pxy_city_digital_twins/waste_route_not_found.html', {
'subdivision': subdivision
})
# 3) derive center lon/lat from coords (or fallback to steps)
coords = selected.get('coords', [])
if coords:
avg_lon = sum(pt[0] for pt in coords) / len(coords)
avg_lat = sum(pt[1] for pt in coords) / len(coords)
else:
steps = selected.get('steps', [])
avg_lon = sum(s['position'][0] for s in steps) / len(steps)
avg_lat = sum(s['position'][1] for s in steps) / len(steps)
# 4) generate your OSMbased city around that center
city_data = generate_osm_city_data(avg_lat, avg_lon)
# 5) sanitize building heights (replace NaN or missing with default 10.0)
default_height = 10.0
for b in city_data.get('buildings', []):
h = b.get('height')
if not isinstance(h, (int, float)) or (isinstance(h, float) and math.isnan(h)):
b['height'] = default_height
# 6) project all coords (and steps) to Web Mercator, recenter them
transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
# route
merc = [transformer.transform(lon, lat) for lon, lat in coords]
if not merc:
merc = [transformer.transform(*s['position']) for s in selected.get('steps', [])]
avg_x = sum(x for x, _ in merc) / len(merc)
avg_z = sum(z for _, z in merc) / len(merc)
rel_coords = [[x - avg_x, z - avg_z] for x, z in merc]
# steps
step_positions = []
for step in selected.get('steps', []):
lon, lat = step['position']
x, z = transformer.transform(lon, lat)
step_positions.append({
'x': x - avg_x,
'z': z - avg_z,
'step': step
})
# 7) render
return render(request, 'pxy_city_digital_twins/virtual_waste_route.html', {
'subdivision': subdivision,
'vehicle': vehicle,
'selected_route_json': json.dumps(selected),
'city_data': city_data,
'rel_coords': rel_coords,
'step_positions': step_positions,
})
def augmented_waste_route(request, subdivision, vehicle):
"""
URL: /waste/<subdivision>/<vehicle>/
Renders a single vehicle's wastecollection route in VR,
overlaid on an OSMgenerated city around the route center.
"""
# 1) load all routes for this subdivision
dispatch_data = get_dispatch_data_for(subdivision)
if not dispatch_data:
return render(request, 'pxy_city_digital_twins/waste_route_default.html', {
'subdivision': subdivision
})
# 2) pick your route by the required `vehicle` path-param
try:
selected = next(r for r in dispatch_data if r['route_id'] == vehicle)
except StopIteration:
return render(request, 'pxy_city_digital_twins/waste_route_not_found.html', {
'subdivision': subdivision
})
# 3) derive center lon/lat from coords (or fallback to steps)
coords = selected.get('coords', [])
if coords:
avg_lon = sum(pt[0] for pt in coords) / len(coords)
avg_lat = sum(pt[1] for pt in coords) / len(coords)
else:
steps = selected.get('steps', [])
avg_lon = sum(s['position'][0] for s in steps) / len(steps)
avg_lat = sum(s['position'][1] for s in steps) / len(steps)
# 4) generate your OSMbased city around that center
city_data = generate_osm_city_data(avg_lat, avg_lon)
# 5) sanitize building heights (replace NaN or missing with default 10.0)
default_height = 10.0
for b in city_data.get('buildings', []):
h = b.get('height')
if not isinstance(h, (int, float)) or (isinstance(h, float) and math.isnan(h)):
b['height'] = default_height
# 6) project all coords (and steps) to Web Mercator, recenter them
transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
# route
merc = [transformer.transform(lon, lat) for lon, lat in coords]
if not merc:
merc = [transformer.transform(*s['position']) for s in selected.get('steps', [])]
avg_x = sum(x for x, _ in merc) / len(merc)
avg_z = sum(z for _, z in merc) / len(merc)
rel_coords = [[x - avg_x, z - avg_z] for x, z in merc]
# steps
step_positions = []
for step in selected.get('steps', []):
lon, lat = step['position']
x, z = transformer.transform(lon, lat)
step_positions.append({
'x': x - avg_x,
'z': z - avg_z,
'step': step
})
# 7) render
return render(request, 'pxy_city_digital_twins/augmented_waste_route.html', {
'subdivision': subdivision,
'vehicle': vehicle,
'selected_route_json': json.dumps(selected),
'city_data': city_data,
'rel_coords': rel_coords,
'step_positions': step_positions,
})