help@rskworld.in +91 93305 39277
RSK World
  • Home
  • Development
    • Web Development
    • Mobile Apps
    • Software
    • Games
    • Project
  • Technologies
    • Data Science
    • AI Development
    • Cloud Development
    • Blockchain
    • Cyber Security
    • Dev Tools
    • Testing Tools
  • Blog
  • About
  • Contact

Theme Settings

Color Scheme
Display Options
Font Size
100%
Back to Project
RSK World
real-estate-bot
/
src
RSK World
real-estate-bot
Real Estate Bot - Python + Flask + OpenAI + SQLite + Property Search + AI Chatbot + Viewing Scheduler
src
  • __pycache__
  • __init__.py476 B
  • ai_recommendation_engine.py20.6 KB
  • app.py7.8 KB
  • blockchain_integration.py1.5 KB
  • chatbot.py15.5 KB
  • database.py18.4 KB
  • image_enhancer.py7.9 KB
  • multilang_support.py8.8 KB
  • neighborhood_analyzer.py6.1 KB
  • price_prediction_engine.py25.1 KB
  • property_search.py15.6 KB
  • virtual_tour_manager.py21.8 KB
  • voice_assistant.py27.6 KB
virtual_tour_manager.py
src/virtual_tour_manager.py
Raw Download
Find: Go to:
"""
Virtual Property Tour Manager with 360° View Support
Author: RSK World (https://rskworld.in)
Founded by: Molla Samser
Designer & Tester: Rima Khatun
Contact: info@rskworld.com, +91 93305 39277
Year: 2026
Description: Advanced virtual tour system with 360° panoramic views and interactive features
"""

import os
import json
import base64
from typing import List, Dict, Any, Optional
from datetime import datetime
import cv2
import numpy as np
from PIL import Image, ImageEnhance
import io

class VirtualTourManager:
    def __init__(self, database_manager):
        """
        Initialize Virtual Tour Manager
        
        Args:
            database_manager: Database manager instance
        """
        self.db_manager = database_manager
        self.tour_data_dir = 'data/virtual_tours'
        self.panorama_dir = 'data/panoramas'
        os.makedirs(self.tour_data_dir, exist_ok=True)
        os.makedirs(self.panorama_dir, exist_ok=True)
        
        # Supported formats for virtual tours
        self.supported_formats = {
            '360_image': ['.jpg', '.jpeg', '.png'],
            '360_video': ['.mp4', '.webm', '.mov'],
            '3d_model': ['.glb', '.gltf', '.obj'],
            'floor_plan': ['.svg', '.png', '.jpg']
        }
    
    def create_virtual_tour(self, property_id: int, tour_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Create a new virtual tour for a property
        
        Args:
            property_id: Property ID
            tour_data: Tour configuration data
            
        Returns:
            Created tour information
        """
        tour_id = f"tour_{property_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
        
        tour_config = {
            'tour_id': tour_id,
            'property_id': property_id,
            'created_at': datetime.now().isoformat(),
            'title': tour_data.get('title', f'Virtual Tour - Property {property_id}'),
            'description': tour_data.get('description', ''),
            'scenes': [],
            'navigation': tour_data.get('navigation', {}),
            'hotspots': tour_data.get('hotspots', []),
            'settings': {
                'auto_rotate': tour_data.get('auto_rotate', True),
                'start_scene': tour_data.get('start_scene', 0),
                'transition_speed': tour_data.get('transition_speed', 1.0),
                'enable_controls': tour_data.get('enable_controls', True),
                'fullscreen_enabled': tour_data.get('fullscreen_enabled', True)
            }
        }
        
        # Process scenes
        for i, scene in enumerate(tour_data.get('scenes', [])):
            processed_scene = self._process_scene(scene, i, tour_id)
            tour_config['scenes'].append(processed_scene)
        
        # Save tour configuration
        tour_file = os.path.join(self.tour_data_dir, f'{tour_id}.json')
        with open(tour_file, 'w') as f:
            json.dump(tour_config, f, indent=2)
        
        return {
            'success': True,
            'tour_id': tour_id,
            'message': 'Virtual tour created successfully',
            'tour_config': tour_config
        }
    
    def _process_scene(self, scene: Dict[str, Any], scene_index: int, tour_id: str) -> Dict[str, Any]:
        """Process individual scene for virtual tour"""
        processed_scene = {
            'scene_id': f"scene_{scene_index}",
            'name': scene.get('name', f'Scene {scene_index + 1}'),
            'type': scene.get('type', '360_image'),
            'media_path': scene.get('media_path', ''),
            'thumbnail': scene.get('thumbnail', ''),
            'hotspots': scene.get('hotspots', []),
            'navigation': {
                'look_at': scene.get('look_at', [0, 0, 0]),
                'camera_position': scene.get('camera_position', [0, 0, 5]),
                'fov': scene.get('fov', 75)
            },
            'lighting': scene.get('lighting', {
                'ambient': 0.6,
                'directional': 0.4,
                'direction': [1, 1, -1]
            }),
            'audio': scene.get('audio', {
                'background_music': '',
                'narration': '',
                'ambient_sounds': []
            })
        }
        
        # Process media based on type
        if processed_scene['type'] == '360_image':
            processed_scene['media_path'] = self._process_360_image(
                scene.get('media_path'), tour_id, scene_index
            )
        elif processed_scene['type'] == '360_video':
            processed_scene['media_path'] = self._process_360_video(
                scene.get('media_path'), tour_id, scene_index
            )
        elif processed_scene['type'] == '3d_model':
            processed_scene['media_path'] = self._process_3d_model(
                scene.get('media_path'), tour_id, scene_index
            )
        
        return processed_scene
    
    def _process_360_image(self, image_path: str, tour_id: str, scene_index: int) -> str:
        """Process 360° panoramic image"""
        if not image_path:
            return ''
        
        try:
            # Load and enhance the image
            image = Image.open(image_path)
            
            # Enhance image quality
            enhancer = ImageEnhance.Brightness(image)
            image = enhancer.enhance(1.1)
            
            enhancer = ImageEnhance.Contrast(image)
            image = enhancer.enhance(1.1)
            
            enhancer = ImageEnhance.Sharpness(image)
            image = enhancer.enhance(1.1)
            
            # Save processed image
            processed_filename = f'{tour_id}_scene_{scene_index}_360.jpg'
            processed_path = os.path.join(self.panorama_dir, processed_filename)
            image.save(processed_path, 'JPEG', quality=95)
            
            return f'panoramas/{processed_filename}'
            
        except Exception as e:
            print(f"Error processing 360 image: {e}")
            return image_path
    
    def _process_360_video(self, video_path: str, tour_id: str, scene_index: int) -> str:
        """Process 360° video"""
        if not video_path:
            return ''
        
        # For video processing, we would typically use FFmpeg
        # For now, return the original path
        return video_path
    
    def _process_3d_model(self, model_path: str, tour_id: str, scene_index: int) -> str:
        """Process 3D model"""
        if not model_path:
            return ''
        
        # For 3D model processing, we would use tools like Blender
        # For now, return the original path
        return model_path
    
    def get_virtual_tour(self, tour_id: str) -> Optional[Dict[str, Any]]:
        """Get virtual tour configuration"""
        tour_file = os.path.join(self.tour_data_dir, f'{tour_id}.json')
        
        if not os.path.exists(tour_file):
            return None
        
        try:
            with open(tour_file, 'r') as f:
                tour_config = json.load(f)
            
            return tour_config
            
        except Exception as e:
            print(f"Error loading tour {tour_id}: {e}")
            return None
    
    def generate_tour_viewer_html(self, tour_id: str) -> str:
        """Generate HTML for virtual tour viewer"""
        tour_config = self.get_virtual_tour(tour_id)
        
        if not tour_config:
            return "<div>Tour not found</div>"
        
        html_template = f"""
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{tour_config['title']} - Virtual Tour</title>
    <script src="https://aframe.io/releases/1.3.0/aframe.min.js"></script>
    <script src="https://unpkg.com/aframe-event-set-component@5.0.0/dist/aframe-event-set-component.min.js"></script>
    <style>
        body {{
            margin: 0;
            font-family: Arial, sans-serif;
            overflow: hidden;
        }}
        
        .tour-controls {{
            position: fixed;
            top: 20px;
            left: 20px;
            z-index: 1000;
            background: rgba(0,0,0,0.7);
            color: white;
            padding: 15px;
            border-radius: 10px;
            max-width: 300px;
        }}
        
        .scene-selector {{
            margin-bottom: 15px;
        }}
        
        .scene-btn {{
            display: block;
            width: 100%;
            margin: 5px 0;
            padding: 8px;
            background: #3498db;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }}
        
        .scene-btn:hover {{
            background: #2980b9;
        }}
        
        .scene-btn.active {{
            background: #e74c3c;
        }}
        
        .tour-info {{
            position: fixed;
            bottom: 20px;
            right: 20px;
            background: rgba(0,0,0,0.7);
            color: white;
            padding: 15px;
            border-radius: 10px;
            max-width: 300px;
        }}
        
        .hotspot {{
            position: absolute;
            width: 30px;
            height: 30px;
            background: #e74c3c;
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-weight: bold;
            z-index: 100;
        }}
        
        .hotspot-info {{
            position: absolute;
            background: white;
            color: black;
            padding: 10px;
            border-radius: 5px;
            display: none;
            z-index: 101;
            max-width: 200px;
        }}
    </style>
</head>
<body>
    <div class="tour-controls">
        <h3>Virtual Tour Controls</h3>
        <div class="scene-selector">
            <h4>Scenes:</h4>
            {self._generate_scene_buttons(tour_config['scenes'])}
        </div>
        <div class="tour-settings">
            <label>
                <input type="checkbox" id="autoRotate" checked> Auto Rotate
            </label>
            <br>
            <label>
                <input type="checkbox" id="fullscreen"> Fullscreen
            </label>
        </div>
    </div>
    
    <div class="tour-info">
        <h4>{tour_config['title']}</h4>
        <p>{tour_config['description']}</p>
        <small>Use mouse to look around, scroll to zoom</small>
    </div>
    
    <a-scene embedded style="height: 100vh; width: 100vw;">
        <a-assets>
            {self._generate_scene_assets(tour_config['scenes'])}
        </a-assets>
        
        {self._generate_scene_elements(tour_config['scenes'])}
        
        <a-sky id="sky"></a-sky>
        
        <a-camera id="camera" 
                  look-controls="enabled: true"
                  wasd-controls="enabled: false">
        </a-camera>
    </a-scene>
    
    <script>
        let currentScene = 0;
        const scenes = {json.dumps([scene['scene_id'] for scene in tour_config['scenes']])};
        const tourConfig = {json.dumps(tour_config)};
        
        function loadScene(sceneIndex) {{
            const scene = tourConfig.scenes[sceneIndex];
            const sky = document.querySelector('#sky');
            
            if (scene.type === '360_image') {{
                sky.setAttribute('src', `#${{scene.scene_id}}_image`);
            }} else if (scene.type === '360_video') {{
                sky.setAttribute('src', `#${{scene.scene_id}}_video`);
            }}
            
            // Update camera position
            const camera = document.querySelector('#camera');
            camera.setAttribute('position', scene.navigation.camera_position.join(' '));
            camera.setAttribute('look-at', scene.navigation.look_at.join(' '));
            
            // Update active button
            document.querySelectorAll('.scene-btn').forEach((btn, index) => {{
                btn.classList.toggle('active', index === sceneIndex);
            }});
            
            currentScene = sceneIndex;
        }}
        
        function setupHotspots() {{
            const scene = tourConfig.scenes[currentScene];
            
            // Remove existing hotspots
            document.querySelectorAll('.hotspot').forEach(el => el.remove());
            
            // Add hotspots for current scene
            scene.hotspots.forEach((hotspot, index) => {{
                const hotspotEl = document.createElement('div');
                hotspotEl.className = 'hotspot';
                hotspotEl.style.left = hotspot.position[0] + '%';
                hotspotEl.style.top = hotspot.position[1] + '%';
                hotspotEl.textContent = index + 1;
                
                const infoEl = document.createElement('div');
                infoEl.className = 'hotspot-info';
                infoEl.innerHTML = `
                    <strong>${{hotspot.title}}</strong><br>
                    ${{hotspot.description}}
                `;
                
                hotspotEl.appendChild(infoEl);
                
                hotspotEl.addEventListener('mouseenter', () => {{
                    infoEl.style.display = 'block';
                }});
                
                hotspotEl.addEventListener('mouseleave', () => {{
                    infoEl.style.display = 'none';
                }});
                
                if (hotspot.action) {{
                    hotspotEl.addEventListener('click', () => {{
                        if (hotspot.action.type === 'navigate_to_scene') {{
                            loadScene(hotspot.action.scene_index);
                        }}
                    }});
                }}
                
                document.body.appendChild(hotspotEl);
            }});
        }}
        
        // Initialize
        document.addEventListener('DOMContentLoaded', () => {{
            loadScene(0);
            setupHotspots();
            
            // Setup scene buttons
            document.querySelectorAll('.scene-btn').forEach((btn, index) => {{
                btn.addEventListener('click', () => loadScene(index));
            }});
            
            // Setup controls
            document.getElementById('autoRotate').addEventListener('change', (e) => {{
                const camera = document.querySelector('#camera');
                if (e.target.checked) {{
                    camera.setAttribute('animation', 'property: rotation; to: 0 360 0; dur: 60s; loop: true');
                }} else {{
                    camera.removeAttribute('animation');
                }}
            }});
            
            document.getElementById('fullscreen').addEventListener('change', (e) => {{
                if (e.target.checked) {{
                    document.documentElement.requestFullscreen();
                }} else {{
                    document.exitFullscreen();
                }}
            }});
        }});
    </script>
</body>
</html>
        """
        
        return html_template
    
    def _generate_scene_buttons(self, scenes: List[Dict[str, Any]]) -> str:
        """Generate HTML buttons for scene selection"""
        buttons = []
        for i, scene in enumerate(scenes):
            active_class = 'active' if i == 0 else ''
            buttons.append(f'<button class="scene-btn {active_class}" onclick="loadScene({i})">{scene["name"]}</button>')
        return '\n'.join(buttons)
    
    def _generate_scene_assets(self, scenes: List[Dict[str, Any]]) -> str:
        """Generate A-Frame assets for scenes"""
        assets = []
        for scene in scenes:
            if scene['type'] == '360_image':
                assets.append(f'<img id="{scene["scene_id"]}_image" src="{scene["media_path"]}" crossorigin="anonymous">')
            elif scene['type'] == '360_video':
                assets.append(f'<video id="{scene["scene_id"]}_video" src="{scene["media_path"]}" autoplay loop muted crossorigin="anonymous"></video>')
        return '\n'.join(assets)
    
    def _generate_scene_elements(self, scenes: List[Dict[str, Any]]) -> str:
        """Generate A-Frame scene elements"""
        elements = []
        for scene in scenes:
            # Add lighting
            if scene.get('lighting'):
                lighting = scene['lighting']
                direction = lighting.get('direction', [1, 1, -1])
                direction_str = ' '.join(map(str, direction))
                elements.append(f'''
                    <a-light type="ambient" color="#fff" intensity="{lighting.get('ambient', 0.6)}"></a-light>
                    <a-light type="directional" color="#fff" intensity="{lighting.get('directional', 0.4)}" 
                             position="{direction_str}"></a-light>
                ''')
        return '\n'.join(elements)
    
    def create_interactive_floor_plan(self, property_id: int, floor_plan_data: Dict[str, Any]) -> Dict[str, Any]:
        """Create interactive floor plan with hotspots"""
        floor_plan_id = f"floor_plan_{property_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
        
        floor_plan_config = {
            'floor_plan_id': floor_plan_id,
            'property_id': property_id,
            'created_at': datetime.now().isoformat(),
            'title': floor_plan_data.get('title', f'Floor Plan - Property {property_id}'),
            'image_path': floor_plan_data.get('image_path', ''),
            'dimensions': floor_plan_data.get('dimensions', {}),
            'rooms': floor_plan_data.get('rooms', []),
            'hotspots': floor_plan_data.get('hotspots', []),
            'measurements': floor_plan_data.get('measurements', {}),
            'interactive_features': {
                'enable_room_hover': floor_plan_data.get('enable_room_hover', True),
                'enable_measurement_tool': floor_plan_data.get('enable_measurement_tool', True),
                'enable_3d_view': floor_plan_data.get('enable_3d_view', True),
                'enable_furniture_placement': floor_plan_data.get('enable_furniture_placement', False)
            }
        }
        
        # Save floor plan configuration
        floor_plan_file = os.path.join(self.tour_data_dir, f'{floor_plan_id}.json')
        with open(floor_plan_file, 'w') as f:
            json.dump(floor_plan_config, f, indent=2)
        
        return {
            'success': True,
            'floor_plan_id': floor_plan_id,
            'message': 'Interactive floor plan created successfully',
            'config': floor_plan_config
        }
    
    def generate_ar_overlay_data(self, property_id: int) -> Dict[str, Any]:
        """Generate AR overlay data for mobile AR viewing"""
        property_data = self.db_manager.get_property_by_id(property_id)
        
        if not property_data:
            return {'success': False, 'error': 'Property not found'}
        
        ar_data = {
            'property_id': property_id,
            'ar_markers': [],
            'overlays': [],
            'tracking_data': {
                'marker_type': 'image',
                'marker_size': 0.1,
                'tracking_confidence': 0.8
            }
        }
        
        # Generate AR markers for different rooms
        rooms = ['Living Room', 'Bedroom', 'Kitchen', 'Bathroom']
        for i, room in enumerate(rooms):
            ar_data['ar_markers'].append({
                'marker_id': f'marker_{i}',
                'room_name': room,
                'position': [i * 2, 0, 0],
                'content': {
                    'title': room,
                    'description': f'Virtual {room} view',
                    'media_url': f'ar_content/{property_id}_{room.lower().replace(" ", "_")}.glb',
                    'scale': [1, 1, 1],
                    'rotation': [0, 0, 0]
                }
            })
        
        # Add information overlays
        ar_data['overlays'] = [
            {
                'type': 'price_info',
                'content': f"₹{property_data.get('price', 0):,}",
                'position': [0, 2, 0],
                'style': 'large_text'
            },
            {
                'type': 'property_details',
                'content': f"{property_data.get('bedrooms', 0)}BHK • {property_data.get('area_sqft', 0)} sqft",
                'position': [0, 1.5, 0],
                'style': 'medium_text'
            }
        ]
        
        return {
            'success': True,
            'ar_data': ar_data,
            'message': 'AR data generated successfully'
        }
    
    def get_tour_analytics(self, tour_id: str) -> Dict[str, Any]:
        """Get analytics data for a virtual tour"""
        # This would typically track user interactions, view times, etc.
        # For now, return mock analytics
        
        analytics = {
            'tour_id': tour_id,
            'total_views': 1250,
            'unique_viewers': 890,
            'average_view_time': 245,  # seconds
            'completion_rate': 0.78,
            'most_viewed_scene': 'scene_2',
            'drop_off_points': ['scene_0', 'scene_3'],
            'device_breakdown': {
                'desktop': 0.65,
                'mobile': 0.28,
                'tablet': 0.07
            },
            'interaction_data': {
                'hotspot_clicks': 342,
                'scene_transitions': 1890,
                'fullscreen_usage': 0.45,
                'auto_rotate_usage': 0.72
            }
        }
        
        return analytics
591 lines•21.8 KB
python

About RSK World

Founded by Molla Samser, with Designer & Tester Rima Khatun, RSK World is your one-stop destination for free programming resources, source code, and development tools.

Founder: Molla Samser
Designer & Tester: Rima Khatun

Development

  • Game Development
  • Web Development
  • Mobile Development
  • AI Development
  • Development Tools

Legal

  • Terms & Conditions
  • Privacy Policy
  • Disclaimer

Contact Info

Nutanhat, Mongolkote
Purba Burdwan, West Bengal
India, 713147

+91 93305 39277

hello@rskworld.in
support@rskworld.in

© 2026 RSK World. All rights reserved.

Content used for educational purposes only. View Disclaimer