import gradio as gr import pandas as pd from nomad_data import country_emoji_map, data, terrain_emoji_map df = pd.DataFrame(data) js_func = """ function refresh() { const url = new URL(window.location); if (url.searchParams.get('__theme') !== 'dark') { url.searchParams.set('__theme', 'dark'); window.location.href = url.href; } } """ def style_quality_of_life(val): """Style the Quality of Life column with color gradient from red to green""" if pd.isna(val): return 'background-color: rgba(200, 200, 200, 0.2); color: #999; font-style: italic;' min_val = 5.0 max_val = 9.0 normalized = (val - min_val) / (max_val - min_val) normalized = max(0, min(normalized, 1)) percentage = int(normalized * 100) if normalized < 0.5: start_color = f"rgba(255, {int(255 * (normalized * 2))}, 0, 0.3)" end_color = "rgba(255, 255, 255, 0)" else: start_color = f"rgba({int(255 * (1 - (normalized - 0.5) * 2))}, 255, 0, 0.3)" end_color = "rgba(255, 255, 255, 0)" return f'background: linear-gradient(to right, {start_color} {percentage}%, {end_color} {percentage}%)' def style_internet_speed(val): """Style the Internet Speed column from red (slow) to green (fast)""" if pd.isna(val): return 'background-color: rgba(200, 200, 200, 0.2); color: #999; font-style: italic;' min_val = 20 # Slow internet max_val = 300 # Fast internet normalized = (val - min_val) / (max_val - min_val) normalized = max(0, min(normalized, 1)) percentage = int(normalized * 100) if normalized < 0.5: start_color = f"rgba(255, {int(255 * (normalized * 2))}, 0, 0.3)" end_color = "rgba(255, 255, 255, 0)" else: start_color = f"rgba({int(255 * (1 - (normalized - 0.5) * 2))}, 255, 0, 0.3)" end_color = "rgba(255, 255, 255, 0)" return f'background: linear-gradient(to right, {start_color} {percentage}%, {end_color} {percentage}%)' def style_dataframe(df): """Apply styling to the entire dataframe""" styled_df = df.copy() styled_df['Terrain'] = styled_df['Terrain'].apply(lambda x: terrain_emoji_map.get(x, x) if pd.notna(x) else x) styler = styled_df.style styler = styler.applymap(style_quality_of_life, subset=['Quality of Life']) styler = styler.applymap(style_internet_speed, subset=['Internet Speed (Mbps)']) styler = styler.highlight_null(props='color: #999; font-style: italic; background-color: rgba(200, 200, 200, 0.2)') styler = styler.format({ 'Quality of Life': lambda x: f'{x:.1f}' if pd.notna(x) else 'Data Not Available', 'Internet Speed (Mbps)': lambda x: f'{x:.1f}' if pd.notna(x) else 'Data Not Available', 'Monthly Cost Living (USD)': lambda x: f'${x:.0f}' if pd.notna(x) else 'Data Not Available', 'Visa Length (Months)': lambda x: f'{x:.0f}' if pd.notna(x) else 'Data Not Available', 'Visa Cost (USD)': lambda x: f'${x:.0f}' if pd.notna(x) else 'Data Not Available', 'Growth Trend (5 Years)': lambda x: f'{x}' if pd.notna(x) else 'Data Not Available' }) return styler def filter_data(country, max_cost): """Filter data based on country and maximum cost of living""" filtered_df = df.copy() if country and country != "All": filtered_df = filtered_df[filtered_df["Country"] == country] if max_cost < df["Monthly Cost Living (USD)"].max(): cost_mask = (filtered_df["Monthly Cost Living (USD)"] <= max_cost) | (filtered_df["Monthly Cost Living (USD)"].isna()) filtered_df = filtered_df[cost_mask] return style_dataframe(filtered_df) def get_unique_values(column): unique_values = ["All"] + sorted(df[column].unique().tolist()) return unique_values def get_country_with_emoji(column): choices_with_emoji = ["✈️ All"] for c in df[column].unique(): if c in country_emoji_map: choices_with_emoji.append(country_emoji_map[c]) else: choices_with_emoji.append(c) return sorted(choices_with_emoji) def get_terrain_with_emoji(): terrains = ["✨ All"] for terrain in sorted(df["Terrain"].unique()): if terrain in terrain_emoji_map: terrains.append(terrain_emoji_map[terrain]) return terrains styled_df = style_dataframe(df) with gr.Blocks(js=js_func, css=""" .gradio-container .table-wrap { font-family: 'Inter', sans-serif; } .gradio-container table td, .gradio-container table th { text-align: left; } .gradio-container table th { background-color: #f3f4f6; font-weight: 600; } /* Style for null values */ .null-value { color: #999; font-style: italic; background-color: rgba(200, 200, 200, 0.2); } .title { font-size: 3rem; font-weight: 600; text-align: center; } .app-subtitle { color: rgba(255, 255, 255, 0.8); font-size: 1.2rem; margin-bottom: 15px; } """) as demo: gr.HTML(elem_classes="title", value="🌍") gr.HTML("Graffiti fonts") gr.Markdown("Discover the best places for digital nomads around the globe") with gr.Row(): with gr.Column(scale=1): cost_slider = gr.Slider( minimum=500, maximum=4000, value=4000, step=100, label="💰 Maximum Monthly Cost of Living (USD)" ) min_internet = gr.Slider( minimum=0, maximum=400, value=0, step=10, label="🌐 Minimum Internet Speed (Mbps)" ) min_quality = gr.Slider( minimum=5, maximum=10, value=5, step=0.1, label="⭐ Minimum Quality of Life" ) with gr.Column(scale=1): country_dropdown = gr.Dropdown( choices=get_country_with_emoji("Country"), value="✈️ All", label="🌏 Filter by Country" ) terrain_dropdown = gr.Dropdown( choices=get_terrain_with_emoji(), value="✨ All", label="🏞️ Filter by Terrain" ) with gr.Column(scale=1): visa_filter = gr.CheckboxGroup( choices=["Has Digital Nomad Visa", "Visa Length ≥ 12 Months"], label="🛂 Visa Requirements" ) special_features = gr.CheckboxGroup( choices=["Coastal Cities", "Cultural Hotspots", "Affordable (<$1000/month)"], label="✨ Special Features" ) data_table = gr.Dataframe( value=styled_df, datatype=["str", "str", "str", "number", "number", "number", "str", "number", "number", "str", "str"], max_height=600, interactive=False, show_copy_button=True, show_row_numbers=True, show_search=True, show_fullscreen_button=True, pinned_columns=3, column_widths=[100, 100, 100] ) def process_country_filter(country, cost): if country and country.startswith("✈️ All"): country = "All" else: for emoji_code in ["🇧🇷", "🇭🇺", "🇺🇾", "🇵🇹", "🇬🇪", "🇹🇭", "🇦🇪", "🇪🇸", "🇮🇹", "🇨🇦", "🇨🇴", "🇲🇽", "🇯🇵", "🇰🇷"]: if country and emoji_code in country: country = country.split(" ", 1)[1] break filtered_df = df.copy() if country and country != "All": filtered_df = filtered_df[filtered_df["Country"] == country] if cost < df["Monthly Cost Living (USD)"].max(): cost_mask = (filtered_df["Monthly Cost Living (USD)"] <= cost) & (filtered_df["Monthly Cost Living (USD)"].notna()) filtered_df = filtered_df[cost_mask] return style_dataframe(filtered_df) def apply_advanced_filters(country, cost, min_internet_speed, min_qol, visa_reqs, features, terrain): if country and country.startswith("✈️ All"): country = "All" else: for emoji_code in ["🇧🇷", "🇭🇺", "🇺🇾", "🇵🇹", "🇬🇪", "🇹🇭", "🇦🇪", "🇪🇸", "🇮🇹", "🇨🇦", "🇨🇴", "🇲🇽", "🇯🇵", "🇰🇷"]: if country and emoji_code in country: country = country.split(" ", 1)[1] break if terrain and terrain.startswith("✨ All"): terrain = "All" else: for emoji in ["🏖️", "⛰️", "🏙️", "🏜️", "🌴", "🏝️", "🌲", "🌾"]: if terrain and emoji in terrain: terrain = terrain.split(" ", 1)[1] break filtered_df = df.copy() if country and country != "All": filtered_df = filtered_df[filtered_df["Country"] == country] if cost < df["Monthly Cost Living (USD)"].max(): cost_mask = (filtered_df["Monthly Cost Living (USD)"] <= cost) & (filtered_df["Monthly Cost Living (USD)"].notna()) filtered_df = filtered_df[cost_mask] if min_internet_speed > 0: filtered_df = filtered_df[filtered_df["Internet Speed (Mbps)"] >= min_internet_speed] if min_qol > 5: filtered_df = filtered_df[filtered_df["Quality of Life"] >= min_qol] if "Has Digital Nomad Visa" in visa_reqs: filtered_df = filtered_df[filtered_df["Digital Nomad Visa"] == "Yes"] if "Visa Length ≥ 12 Months" in visa_reqs: filtered_df = filtered_df[filtered_df["Visa Length (Months)"] >= 12] if terrain and terrain != "All": filtered_df = filtered_df[filtered_df["Terrain"] == terrain] if "Coastal Cities" in features: coastal_keywords = ["coast", "beach", "sea", "ocean"] mask = filtered_df["Key Feature"].str.contains("|".join(coastal_keywords), case=False, na=False) filtered_df = filtered_df[mask] if "Cultural Hotspots" in features: cultural_keywords = ["cultur", "art", "histor", "heritage"] mask = filtered_df["Key Feature"].str.contains("|".join(cultural_keywords), case=False, na=False) filtered_df = filtered_df[mask] if "Affordable (<$1000/month)" in features: filtered_df = filtered_df[filtered_df["Monthly Cost Living (USD)"] < 1000] return style_dataframe(filtered_df) country_dropdown.change( apply_advanced_filters, [country_dropdown, cost_slider, min_internet, min_quality, visa_filter, special_features, terrain_dropdown], data_table ) cost_slider.change( apply_advanced_filters, [country_dropdown, cost_slider, min_internet, min_quality, visa_filter, special_features, terrain_dropdown], data_table ) min_internet.change( apply_advanced_filters, [country_dropdown, cost_slider, min_internet, min_quality, visa_filter, special_features, terrain_dropdown], data_table ) min_quality.change( apply_advanced_filters, [country_dropdown, cost_slider, min_internet, min_quality, visa_filter, special_features, terrain_dropdown], data_table ) visa_filter.change( apply_advanced_filters, [country_dropdown, cost_slider, min_internet, min_quality, visa_filter, special_features, terrain_dropdown], data_table ) special_features.change( apply_advanced_filters, [country_dropdown, cost_slider, min_internet, min_quality, visa_filter, special_features, terrain_dropdown], data_table ) terrain_dropdown.change( apply_advanced_filters, [country_dropdown, cost_slider, min_internet, min_quality, visa_filter, special_features, terrain_dropdown], data_table ) demo.launch()