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