add trees data

This commit is contained in:
Gal 2025-06-20 22:47:47 +02:00
parent 74baba4a7e
commit c14f5ead38
Signed by: gal
GPG Key ID: F035BC65003BC00B
1 changed files with 83 additions and 89 deletions

View File

@ -99,9 +99,12 @@ class ScoringEngine:
if not weights:
raise ValueError(f"Unknown personality type: {personality}")
# Calculate component scores
# Pre-fetch tree data once for all calculations
tree_data = await self._fetch_tree_data_once(green_space)
# Calculate component scores with cached tree data
component_scores = await self._calculate_component_scores(
green_space, personality, user_location
green_space, personality, user_location, tree_data
)
# Calculate weighted final score
@ -134,11 +137,26 @@ class ScoringEngine:
recommendations=recommendations
)
async def _fetch_tree_data_once(self, green_space: GreenSpace) -> Optional[Any]:
"""Fetch tree data once and reuse for all calculations."""
try:
# Use the largest radius needed across all methods (400m)
tree_response = await self.street_tree_service.get_trees_near_location(
green_space.coordinates.lat,
green_space.coordinates.lng,
radius_m=400
)
return tree_response
except Exception as e:
print(f"Error fetching tree data: {e}")
return None
async def _calculate_component_scores(
self,
green_space: GreenSpace,
personality: str,
user_location: Optional[Tuple[float, float]] = None
user_location: Optional[Tuple[float, float]] = None,
tree_data: Optional[Any] = None
) -> Dict[str, int]:
"""Calculate individual component scores."""
scores = {}
@ -151,7 +169,7 @@ class ScoringEngine:
# Personality-specific components
if personality == "little_adventurers":
scores["playground_quality"] = green_space.recreation.playground_quality
scores["shade_quality"] = await self._score_shade_quality_with_trees(green_space)
scores["shade_quality"] = self._score_shade_quality_with_trees(green_space, tree_data)
scores["toilet_proximity"] = await self._score_toilet_proximity(green_space)
scores["family_amenities"] = await self._score_family_amenities(green_space)
@ -171,11 +189,11 @@ class ScoringEngine:
elif personality == "zen_masters":
scores["quietness"] = self._score_quietness(green_space)
scores["nature_immersion"] = await self._score_nature_immersion_with_trees(green_space)
scores["nature_immersion"] = self._score_nature_immersion_with_trees(green_space, tree_data)
scores["crowd_density"] = await self._score_crowd_density(green_space)
scores["water_features"] = 100 if green_space.environmental.water_features else 0
scores["meditation_spots"] = await self._score_meditation_spots_with_trees(green_space)
scores["air_quality"] = await self._score_air_quality_with_trees(green_space)
scores["meditation_spots"] = self._score_meditation_spots_with_trees(green_space, tree_data)
scores["air_quality"] = self._score_air_quality_with_trees(green_space, tree_data)
elif personality == "active_lifestyle":
scores["fitness_facilities"] = 100 if green_space.recreation.sports_facilities else 0
@ -184,11 +202,11 @@ class ScoringEngine:
scores["terrain_variety"] = self._score_terrain_variety(green_space)
elif personality == "wildlife_lover":
scores["wildlife_diversity"] = await self._score_wildlife_diversity_with_trees(green_space)
scores["natural_habitat"] = await self._score_natural_habitat_with_trees(green_space)
scores["wildlife_diversity"] = self._score_wildlife_diversity_with_trees(green_space, tree_data)
scores["natural_habitat"] = self._score_natural_habitat_with_trees(green_space, tree_data)
scores["water_features"] = 100 if green_space.environmental.water_features else 0
scores["tree_coverage"] = await self._score_tree_coverage_with_real_data(green_space)
scores["observation_spots"] = await self._score_observation_spots_with_trees(green_space)
scores["tree_coverage"] = self._score_tree_coverage_with_real_data(green_space, tree_data)
scores["observation_spots"] = self._score_observation_spots_with_trees(green_space, tree_data)
elif personality == "art_nerd":
scores["cultural_proximity"] = await self._score_cultural_proximity(green_space)
@ -391,7 +409,7 @@ class ScoringEngine:
score += 25
return min(100, score)
async def _score_air_quality(self, green_space: GreenSpace) -> int:
def _score_air_quality(self, green_space: GreenSpace) -> int:
"""Score air quality."""
score = green_space.environmental.tree_coverage_percent
if green_space.environmental.natural_surface_percent > 80:
@ -570,7 +588,7 @@ class ScoringEngine:
personality: str,
radius: int
) -> Dict[str, Any]:
"""Score a specific location."""
"""Score a specific location with optimized performance."""
# Check if location is in a green space
green_space = await self.berlin_data.get_green_space_at_location(lat, lng)
@ -582,7 +600,7 @@ class ScoringEngine:
"personality": personality
}
# Score the green space
# Score the green space (this now uses cached tree data internally)
personality_score = await self.score_green_space(green_space, personality, (lat, lng))
return {
@ -628,26 +646,23 @@ class ScoringEngine:
# === ENHANCED TREE-BASED SCORING METHODS ===
async def _score_tree_coverage_with_real_data(self, green_space: GreenSpace) -> int:
def _score_tree_coverage_with_real_data(self, green_space: GreenSpace, tree_data: Optional[Any] = None) -> int:
"""Enhanced tree coverage scoring using real street tree data."""
if not tree_data:
return green_space.environmental.tree_coverage_percent
try:
tree_response = await self.street_tree_service.get_trees_near_location(
green_space.coordinates.lat,
green_space.coordinates.lng,
radius_m=300
)
# Combine base environmental score with real tree data
base_score = green_space.environmental.tree_coverage_percent
tree_shade_coverage = tree_response.shade_analysis.estimated_shade_coverage
tree_shade_coverage = tree_data.shade_analysis.estimated_shade_coverage
# Use the higher of the two scores, with bonus for high tree density
enhanced_score = max(base_score, tree_shade_coverage)
# Bonus for high tree density
if tree_response.metrics.trees_per_hectare > 50:
if tree_data.metrics.trees_per_hectare > 50:
enhanced_score = min(100, enhanced_score + 15)
elif tree_response.metrics.trees_per_hectare > 20:
elif tree_data.metrics.trees_per_hectare > 20:
enhanced_score = min(100, enhanced_score + 10)
return int(enhanced_score)
@ -656,18 +671,15 @@ class ScoringEngine:
print(f"Error enhancing tree coverage score: {e}")
return green_space.environmental.tree_coverage_percent
async def _score_wildlife_diversity_with_trees(self, green_space: GreenSpace) -> int:
def _score_wildlife_diversity_with_trees(self, green_space: GreenSpace, tree_data: Optional[Any] = None) -> int:
"""Enhanced wildlife diversity scoring using real tree species data."""
if not tree_data:
return green_space.environmental.wildlife_diversity_score
try:
tree_response = await self.street_tree_service.get_trees_near_location(
green_space.coordinates.lat,
green_space.coordinates.lng,
radius_m=400
)
base_score = green_space.environmental.wildlife_diversity_score
tree_diversity = tree_response.metrics.species_diversity_score
mature_trees_bonus = min(20, tree_response.metrics.mature_trees_count)
tree_diversity = tree_data.metrics.species_diversity_score
mature_trees_bonus = min(20, tree_data.metrics.mature_trees_count)
# Combine scores with weighting
enhanced_score = int((base_score * 0.6) + (tree_diversity * 0.4) + mature_trees_bonus)
@ -678,23 +690,20 @@ class ScoringEngine:
print(f"Error enhancing wildlife diversity score: {e}")
return green_space.environmental.wildlife_diversity_score
async def _score_shade_quality_with_trees(self, green_space: GreenSpace) -> int:
def _score_shade_quality_with_trees(self, green_space: GreenSpace, tree_data: Optional[Any] = None) -> int:
"""Enhanced shade quality scoring using real tree data."""
if not tree_data:
return green_space.environmental.shade_quality
try:
tree_response = await self.street_tree_service.get_trees_near_location(
green_space.coordinates.lat,
green_space.coordinates.lng,
radius_m=200
)
base_shade = green_space.environmental.shade_quality
tree_shade_quality = tree_response.shade_analysis.shade_quality_score
tree_shade_quality = tree_data.shade_analysis.shade_quality_score
# Use the better of the two scores
enhanced_score = max(base_shade, tree_shade_quality)
# Bonus for large nearby trees
large_trees_count = len(tree_response.shade_analysis.nearby_large_trees)
large_trees_count = len(tree_data.shade_analysis.nearby_large_trees)
if large_trees_count > 5:
enhanced_score = min(100, enhanced_score + 15)
elif large_trees_count > 2:
@ -706,15 +715,12 @@ class ScoringEngine:
print(f"Error enhancing shade quality score: {e}")
return green_space.environmental.shade_quality
async def _score_nature_immersion_with_trees(self, green_space: GreenSpace) -> int:
def _score_nature_immersion_with_trees(self, green_space: GreenSpace, tree_data: Optional[Any] = None) -> int:
"""Enhanced nature immersion scoring using real tree data."""
if not tree_data:
return self._score_nature_immersion(green_space)
try:
tree_response = await self.street_tree_service.get_trees_near_location(
green_space.coordinates.lat,
green_space.coordinates.lng,
radius_m=350
)
# Base score from existing method
base_score = green_space.environmental.tree_coverage_percent
base_score += green_space.environmental.natural_surface_percent // 2
@ -722,9 +728,9 @@ class ScoringEngine:
base_score += 15
# Enhancement from tree data
tree_density_score = min(30, tree_response.metrics.trees_per_hectare)
canopy_density_bonus = int(tree_response.shade_analysis.canopy_density * 20) if tree_response.shade_analysis.canopy_density else 0
species_diversity_bonus = min(15, tree_response.metrics.species_diversity_score // 5)
tree_density_score = min(30, tree_data.metrics.trees_per_hectare)
canopy_density_bonus = int(tree_data.shade_analysis.canopy_density * 20) if tree_data.shade_analysis.canopy_density else 0
species_diversity_bonus = min(15, tree_data.metrics.species_diversity_score // 5)
enhanced_score = base_score + tree_density_score + canopy_density_bonus + species_diversity_bonus
@ -734,23 +740,20 @@ class ScoringEngine:
print(f"Error enhancing nature immersion score: {e}")
return self._score_nature_immersion(green_space)
async def _score_natural_habitat_with_trees(self, green_space: GreenSpace) -> int:
def _score_natural_habitat_with_trees(self, green_space: GreenSpace, tree_data: Optional[Any] = None) -> int:
"""Enhanced natural habitat scoring using real tree data."""
if not tree_data:
return self._score_natural_habitat(green_space)
try:
tree_response = await self.street_tree_service.get_trees_near_location(
green_space.coordinates.lat,
green_space.coordinates.lng,
radius_m=300
)
base_score = green_space.environmental.tree_coverage_percent
base_score += green_space.environmental.natural_surface_percent // 2
if green_space.environmental.water_features:
base_score += 15
# Tree habitat quality factors
mature_trees_score = min(25, tree_response.metrics.mature_trees_count // 2)
species_diversity_score = min(20, tree_response.metrics.species_diversity_score // 3)
mature_trees_score = min(25, tree_data.metrics.mature_trees_count // 2)
species_diversity_score = min(20, tree_data.metrics.species_diversity_score // 3)
enhanced_score = base_score + mature_trees_score + species_diversity_score
@ -760,15 +763,12 @@ class ScoringEngine:
print(f"Error enhancing natural habitat score: {e}")
return self._score_natural_habitat(green_space)
async def _score_observation_spots_with_trees(self, green_space: GreenSpace) -> int:
def _score_observation_spots_with_trees(self, green_space: GreenSpace, tree_data: Optional[Any] = None) -> int:
"""Enhanced wildlife observation scoring using real tree data."""
if not tree_data:
return self._score_observation_spots(green_space)
try:
tree_response = await self.street_tree_service.get_trees_near_location(
green_space.coordinates.lat,
green_space.coordinates.lng,
radius_m=250
)
base_score = green_space.environmental.tree_coverage_percent // 2
if green_space.environmental.water_features:
base_score += 30
@ -776,11 +776,11 @@ class ScoringEngine:
base_score += 20
# Large trees provide better observation opportunities
large_trees_count = len(tree_response.shade_analysis.nearby_large_trees)
large_trees_count = len(tree_data.shade_analysis.nearby_large_trees)
observation_bonus = min(25, large_trees_count * 3)
# Species diversity attracts more wildlife to observe
diversity_bonus = min(15, tree_response.metrics.species_diversity_score // 4)
diversity_bonus = min(15, tree_data.metrics.species_diversity_score // 4)
enhanced_score = base_score + observation_bonus + diversity_bonus
@ -790,15 +790,12 @@ class ScoringEngine:
print(f"Error enhancing observation spots score: {e}")
return self._score_observation_spots(green_space)
async def _score_meditation_spots_with_trees(self, green_space: GreenSpace) -> int:
def _score_meditation_spots_with_trees(self, green_space: GreenSpace, tree_data: Optional[Any] = None) -> int:
"""Enhanced meditation spots scoring using real tree data."""
if not tree_data:
return self._score_meditation_spots(green_space)
try:
tree_response = await self.street_tree_service.get_trees_near_location(
green_space.coordinates.lat,
green_space.coordinates.lng,
radius_m=200
)
base_score = green_space.environmental.tree_coverage_percent // 2
if green_space.environmental.water_features:
base_score += 25
@ -806,8 +803,8 @@ class ScoringEngine:
base_score += 25
# Trees enhance meditation through natural sounds and shade
shade_quality_bonus = min(20, tree_response.shade_analysis.shade_quality_score // 4)
canopy_bonus = int(tree_response.shade_analysis.canopy_density * 15) if tree_response.shade_analysis.canopy_density else 0
shade_quality_bonus = min(20, tree_data.shade_analysis.shade_quality_score // 4)
canopy_bonus = int(tree_data.shade_analysis.canopy_density * 15) if tree_data.shade_analysis.canopy_density else 0
enhanced_score = base_score + shade_quality_bonus + canopy_bonus
@ -817,22 +814,19 @@ class ScoringEngine:
print(f"Error enhancing meditation spots score: {e}")
return self._score_meditation_spots(green_space)
async def _score_air_quality_with_trees(self, green_space: GreenSpace) -> int:
def _score_air_quality_with_trees(self, green_space: GreenSpace, tree_data: Optional[Any] = None) -> int:
"""Enhanced air quality scoring using real tree data."""
if not tree_data:
return self._score_air_quality(green_space)
try:
tree_response = await self.street_tree_service.get_trees_near_location(
green_space.coordinates.lat,
green_space.coordinates.lng,
radius_m=400
)
base_score = green_space.environmental.tree_coverage_percent
if green_space.environmental.natural_surface_percent > 80:
base_score += 20
# More trees = better air quality
tree_density_bonus = min(25, tree_response.metrics.trees_per_hectare // 2)
mature_trees_bonus = min(15, tree_response.metrics.mature_trees_count // 3)
tree_density_bonus = min(25, tree_data.metrics.trees_per_hectare // 2)
mature_trees_bonus = min(15, tree_data.metrics.mature_trees_count // 3)
enhanced_score = base_score + tree_density_bonus + mature_trees_bonus
@ -840,4 +834,4 @@ class ScoringEngine:
except Exception as e:
print(f"Error enhancing air quality score: {e}")
return await self._score_air_quality(green_space)
return self._score_air_quality(green_space)