# bali_map.py # Creates an interactive HTML map of Bali (and nearby islands) with readable, always-visible labels. import folium DESTINATIONS = { "Sacred Monkey Forest": ( -8.5187511, 115.2585973, ), # :contentReference[oaicite:0]{index=0} "Uluwatu Temple": ( -8.8291432, 115.0849069, ), # :contentReference[oaicite:1]{index=1} "Sanur Beach": (-8.673889, 115.263611), # :contentReference[oaicite:2]{index=2} "Tanah Lot Temple": ( -8.618786, 115.086733, ), # :contentReference[oaicite:3]{index=3} "Seminyak Beach": (-8.6925, 115.158611), # :contentReference[oaicite:4]{index=4} "Nusa Dua": (-8.791918, 115.225375), # :contentReference[oaicite:5]{index=5} "Bali Zoo": (-8.59128, 115.26456), # :contentReference[oaicite:6]{index=6} "Mount Batur": (-8.23889, 115.37750), # :contentReference[oaicite:7]{index=7} "Ulun Danu Bratan": ( -8.275177, 115.1668487, ), # :contentReference[oaicite:8]{index=8} "Tirta Gangga": (-8.411944, 115.5875), # :contentReference[oaicite:9]{index=9} "Pandawa Beach": (-8.84586, 115.18417), # :contentReference[oaicite:10]{index=10} "Jimbaran Bay": (-8.79093, 115.16006), # :contentReference[oaicite:11]{index=11} "Double Six Beach": ( -8.6975074, 115.1610332, ), # :contentReference[oaicite:12]{index=12} "Devil Tears": (-8.6905650, 115.4302884), # :contentReference[oaicite:13]{index=13} "Kelingking Beach": ( -8.750644, 115.474693, ), # :contentReference[oaicite:14]{index=14} "Lempuyang Temple": ( -8.395195, 115.647885, ), # :contentReference[oaicite:15]{index=15} "Canggu Beach": (-8.639877, 115.140172), # :contentReference[oaicite:16]{index=16} "Mount Agung": (-8.340686, 115.503622), # :contentReference[oaicite:17]{index=17} } # --- Map base --- m = folium.Map( location=(-8.45, 115.20), zoom_start=9, tiles="CartoDB positron", control_scale=True, zoom_snap=0.1, zoom_delta=0.1, max_zoom=18, ) # --- Label styling (readable, always visible) --- LABEL_STYLE = """ padding: 3px 6px; font-size: 16px; font-weight: 600; color: #111; white-space: nowrap; """ # Per-label pixel offsets (x, y). Positive y moves the label down. LABEL_OFFSETS = { "Nusa Dua": (0, 20), "Double Six Beach": (0, 20), } def add_point_with_label(name: str, lat: float, lon: float): # Small dot at the exact coordinate folium.CircleMarker( location=(lat, lon), radius=4, weight=2, fill=True, fill_opacity=1.0, tooltip=name, # still useful on hover ).add_to(m) # Slightly offset label so it doesn't sit directly on the dot offset_x, offset_y = LABEL_OFFSETS.get(name, (0, 0)) base_anchor_x, base_anchor_y = (-8, 12) folium.Marker( location=(lat, lon), icon=folium.DivIcon( icon_size=(1, 1), icon_anchor=( base_anchor_x + offset_x, base_anchor_y - offset_y, ), # pixel offset: left/up relative to point html=f'
{name}
', ), ).add_to(m) # Add all destinations lats, lons = [], [] for name, (lat, lon) in DESTINATIONS.items(): add_point_with_label(name, lat, lon) lats.append(lat) lons.append(lon) # Fit map bounds to include Nusa Penida / Lembongan as well pad = 0.005 m.fit_bounds([[min(lats) - pad, min(lons) - pad], [max(lats) + pad, max(lons) + pad]]) # Output out_file = "bali_destinations_labeled.html" m.save(out_file) print(f"Saved: {out_file}")