B4J Code Snippet [PyBridge] Using folium (OpenStreetMap) in a webview

Daestrum

Expert
Licensed User
Longtime User
Is there a way to save the map without resetting its location and zoom level?
Sorry only just saw this question. I will have a look, its possible (I think) to get the centre coord of the view and the zoom level, so the next call to save the map can use that.

I will have a play and see what comes up.

I have had thoughts on the ablity (or lack of) to remove markers/lines etc from the map. As in post #19 I found you dont need to save the map, you can actually send the html back to B4J. So in theory you could edit the html to remove markers etc.
 

Ralph Parkhurst

Member
Licensed User
Longtime User
... get the centre coord of the view and the zoom level ...

I do hope you can find this data Daestrum.

From my online research, Folium is a terrific visualiser, however returning useful data out of it is extremely difficult. As an example, I wanted to return the lat/lon coordinates of a polygon I had drawn. The only way I could find was to use Folium's 'export' function which places the geojson data into the clipboard, then I read it in B4J using fx.Clipboard.GetString. Its clumsy, but it works.

For what it's worth, here's the way I store polygons and markers persistently and apply ID codes to facilitate removal or update. It is all based upon Daestrum's original tutorial code...

folium_test3a.py:
import folium
from folium.plugins import MarkerCluster, Draw, MousePosition
import json
import os

# File paths for persistence
MARKERS_FILE = "markers.json"
POLYGONS_FILE = "polygons.json"
MAP_FILE = "map.html"

# Global variable to store the map instance
#map_EWS = None
global map_EWS


# Load markers and polygons
def load_markers():
    if os.path.exists(MARKERS_FILE):
        with open(MARKERS_FILE, "r") as f:
            try:
                return json.load(f)
            except json.JSONDecodeError:
                return {}
    return {}


def save_markers():
    with open(MARKERS_FILE, "w") as f:
        json.dump(markers, f, indent=4)


def load_polygons():
    if os.path.exists(POLYGONS_FILE):
        with open(POLYGONS_FILE, "r") as f:
            try:
                return json.load(f)
            except json.JSONDecodeError:
                return {}
    return {}


def save_polygons():
    with open(POLYGONS_FILE, "w") as f:
        json.dump(polygons, f, indent=4)

markers = load_markers()
polygons = load_polygons()


# Start map (Called once at entry point)
def start_map(lat, lon, zoom):
    global map_EWS
    global current_lat, current_lon, current_zoom
    current_lat, current_lon, current_zoom = lat, lon, zoom
    print(f"📍 Starting new map at: lat={current_lat}, lon={current_lon}, zoom={current_zoom}")
    regenerate_map()


# Regenerate map
def regenerate_map():
    global map_EWS
  
    map_EWS = folium.Map(location=[current_lat,current_lon], zoom_start=current_zoom,tiles="OpenStreetMap")
    marker_cluster = MarkerCluster().add_to(map_EWS)

    formatter = "function(num) {return L.Util.formatNum(num, 4) + ' ° ';};"
    MousePosition(
        position="bottomleft", separator=" | ", empty_string="NaN",
        lng_first=False, num_digits=22, prefix="Coordinates:",
        lat_formatter=formatter, lng_formatter=formatter
    ).add_to(map_EWS)

    for marker_id, data in markers.items():
        lat, lon, popup_text = data["lat"], data["lon"], data["popup_text"]
        folium.Marker(
            location=[lat, lon],
            popup=f"{popup_text} (ID: {marker_id})",
            tooltip=f"Marker {marker_id}"
        ).add_to(marker_cluster)

    for polygon_id, data in polygons.items():
        coordinates, label, color = data["coordinates"], data["label"], data["color"]
        folium.Polygon(
            locations=coordinates,
            color=color,
            weight=3,
            fill=True,
            fill_opacity=0.3,
            fill_color=color,
            popup=f"{label} (ID: {polygon_id})",
            tooltip=f"Polygon {polygon_id}"
        ).add_to(map_EWS)

    draw_options = {
        "polyline": False,
        "polygon": {
            "shapeOptions": {
                "color": "red",
                "weight": 3,
                "fillColor": "blue",
                "fillOpacity": 0.3
            }
        },
        "rectangle": False,
        "circle": False,
        "marker": False,
        "circlemarker": False
    }

    Draw(export=True, position='topleft', filename='data.geojson', draw_options=draw_options, edit_options={'poly': {'allowIntersection': False}}).add_to(map_EWS) 
    save_map()


# Save map
def save_map():
    global map_EWS, current_lat, current_lon, current_zoom
    if map_EWS:
        map_EWS.save(MAP_FILE)
        print(f"✅ Map saved as {MAP_FILE}")
    else:
        print("❌ Error: map_EWS is not initialized.")
    return MAP_FILE


# Marker functions
def add_marker(marker_id, lat, lon, popup_text):
    if marker_id in markers:
        print("Error: Marker ID already exists. Marker not added.")
        return
    markers[marker_id] = {"lat": lat, "lon": lon, "popup_text": popup_text}
    save_markers()
    regenerate_map()
    return save_map()


def update_marker(marker_id, new_lat, new_lon, new_popup_text):
    if marker_id not in markers:
        print("Error: Marker not found. Position not updated.")
        return
    markers[marker_id] = {"lat": new_lat, "lon": new_lon, "popup_text": new_popup_text}
    save_markers()
    regenerate_map()
    return save_map()


def remove_marker(marker_id):
    if marker_id not in markers:
        print("Error: Marker not found. Marker not removed.")
        return
    del markers[marker_id]
    save_markers()
    regenerate_map()
    return save_map()

# Polygon functions
def add_polygon(polygon_id, coordinates, label="Polygon", border_color="red"):
    valid_colors = {"blue", "red", "black", "green"}
    if polygon_id in polygons:
        print("Error: Polygon ID already exists. Polygon not added.")
        return
    if border_color not in valid_colors:
        border_color = "red"
    polygons[polygon_id] = {"coordinates": coordinates, "label": label, "color": border_color}
    save_polygons()
    regenerate_map()
    return save_map()


def update_polygon(polygon_id, new_coordinates, new_label="Polygon", new_border_color="red"):
    valid_colors = {"blue", "red", "black", "green"}
    if polygon_id not in polygons:
        print("Error: Polygon not found. Polygon not updated.")
        return
    polygons[polygon_id]= {"coordinates": new_coordinates, "label": new_label, "color": new_border_color}
    save_polygons()
    regenerate_map()
    return save_map()


def remove_polygon(polygon_id):
    if polygon_id not in polygons:
        print("Error: Polygon not found. Polygon not removed.")
        return
    del polygons[polygon_id]
    save_polygons()
    regenerate_map()
    return save_map()

This is very new to me

Well, it's also brand new to me Javiers
 
Last edited:

javiers

Active Member
Licensed User
Longtime User
It seems that there is some good news regarding the jGoogle Maps 2.01 issue. My programs use it and I was looking for other alternatives. But if this materializes, I will continue to work on that line of work, since it seems that it will not require any changes to the code already written!

jGoogle Maps 2.01 issue...
 

Ralph Parkhurst

Member
Licensed User
Longtime User
Thanks for the detail - Star-Dust is worth his weight in gold if this is a drop-in replacement library