Modify this script so that it creates another lines layer from the buffered roads network at 6.5m and display this aswell. import tkinter as tk
from tkinter import messagebox, filedialog
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import matplotlib.pyplot as plt
import osmnx as ox
import geopandas as gpd
from shapely.geometry import LineString, MultiLineString
from shapely.ops import linemerge, snap
import threading

# Ensure OSMnx uses cache and ignores logging for cleaner output
ox.config(use_cache=True, log_console=False)

class RoadNetworkApp:
    def __init__(self, master):
        self.master = master
        master.title("Road Network Viewer with Interactive Zoom and Enhanced Buffer")

        # Initialize variables to store GeoDataFrames
        self.roads_gdf = None
        self.buffer_lines_gdf = None

        # -------------------------
        # Input Frame
        # -------------------------
        input_frame = tk.Frame(master)
        input_frame.pack(pady=10, padx=10, fill=tk.X)

        label = tk.Label(input_frame, text="Enter Town/City Name:")
        label.pack(side=tk.LEFT, padx=(0, 10))

        self.entry = tk.Entry(input_frame, width=50)
        self.entry.pack(side=tk.LEFT, fill=tk.X, expand=True)

        fetch_button = tk.Button(input_frame, text="Fetch Road Network", command=self.fetch_network_threaded)
        fetch_button.pack(side=tk.LEFT, padx=(10, 0))

        # -------------------------
        # Buffer Width Frame
        # -------------------------
        buffer_frame = tk.Frame(master)
        buffer_frame.pack(pady=5, padx=10, fill=tk.X)

        buffer_label = tk.Label(buffer_frame, text="Buffer Width (meters):")
        buffer_label.pack(side=tk.LEFT, padx=(0, 10))

        self.buffer_width_entry = tk.Entry(buffer_frame, width=10)
        self.buffer_width_entry.pack(side=tk.LEFT)
        self.buffer_width_entry.insert(0, "6.5")  # Default value

        # -------------------------
        # Options Frame
        # -------------------------
        options_frame = tk.Frame(master)
        options_frame.pack(pady=5, padx=10, fill=tk.X)

        self.show_roads_var = tk.BooleanVar(value=True)
        self.show_buffers_var = tk.BooleanVar(value=True)

        roads_checkbox = tk.Checkbutton(
            options_frame,
            text="Show Roads Network",
            variable=self.show_roads_var,
            command=self.update_plot
        )
        roads_checkbox.pack(side=tk.LEFT, padx=5)

        buffers_checkbox = tk.Checkbutton(
            options_frame,
            text="Show Buffer Lines",
            variable=self.show_buffers_var,
            command=self.update_plot
        )
        buffers_checkbox.pack(side=tk.LEFT, padx=5)

        # -------------------------
        # Export Button
        # -------------------------
        export_button = tk.Button(options_frame, text="Export Buffer Lines", command=self.export_buffers)
        export_button.pack(side=tk.LEFT, padx=5)

        # -------------------------
        # Status Label
        # -------------------------
        self.status_label = tk.Label(master, text="Status: Ready", relief=tk.SUNKEN, anchor=tk.W)
        self.status_label.pack(side=tk.BOTTOM, fill=tk.X)

        # -------------------------
        # Plot Frame
        # -------------------------
        self.plot_frame = tk.Frame(master)
        self.plot_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)

        # Initialize Matplotlib Figure
        self.fig, self.ax = plt.subplots(figsize=(10, 8))
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_frame)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

        # Add Navigation Toolbar
        self.toolbar = NavigationToolbar2Tk(self.canvas, self.plot_frame)
        self.toolbar.update()
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)

    def fetch_network_threaded(self):
        threading.Thread(target=self.fetch_network).start()

    def fetch_network(self):
        town = self.entry.get().strip()
        if not town:
            messagebox.showwarning("Input Error", "Please enter a town or city name.")
            return

        try:
            self.status_label.config(text=f"Status: Fetching road network for {town}...")
            self.master.update_idletasks()

            # Fetch road network using OSMnx
            graph = ox.graph_from_place(town, network_type='drive')
            if graph is None or len(graph.edges) == 0:
                raise ValueError("No road network found for the specified location.")

            # Convert graph to GeoDataFrames
            self.roads_gdf, _ = ox.graph_to_gdfs(graph, nodes=True, edges=True)

            # Project to UTM for accurate measurements
            self.roads_gdf = ox.project_gdf(self.roads_gdf)

            # Get buffer width from user input
            try:
                buffer_distance = float(self.buffer_width_entry.get())
                if buffer_distance <= 0:
                    raise ValueError("Buffer width must be a positive number.")
            except ValueError as ve:
                messagebox.showerror("Input Error", f"Invalid buffer width:\n{ve}")
                self.status_label.config(text="Status: Ready")
                return

            self.status_label.config(text=f"Status: Creating {buffer_distance*2}m buffer around roads...")
            self.master.update_idletasks()

            # Create buffer around roads (6.5m on each side, total 13m)
            self.roads_gdf['buffer'] = self.roads_gdf.geometry.buffer(buffer_distance)

            # Merge all buffers into a single geometry
            buffer_union = self.roads_gdf['buffer'].unary_union

            self.status_label.config(text="Status: Processing buffer boundaries...")
            self.master.update_idletasks()

            # Extract buffer boundaries
            buffer_boundary = buffer_union.boundary

            if isinstance(buffer_boundary, (LineString, MultiLineString)):
                # Merge lines to form a coherent network
                merged_lines = linemerge(buffer_boundary)
                # Ensure it's a MultiLineString
                if isinstance(merged_lines, LineString):
                    merged_lines = MultiLineString([merged_lines])

                # Snap buffer lines to road intersections to ensure connectivity
                snapped_lines = snap(merged_lines, self.roads_gdf.unary_union, tolerance=0.1)

                # Create GeoDataFrame for buffer lines
                self.buffer_lines_gdf = gpd.GeoDataFrame(geometry=[snapped_lines], crs=self.roads_gdf.crs)
                # Explode MultiLineStrings into individual LineStrings for better plotting
                self.buffer_lines_gdf = self.buffer_lines_gdf.explode().reset_index(drop=True)
            else:
                self.buffer_lines_gdf = None

            self.status_label.config(text="Status: Plotting data...")
            self.master.update_idletasks()

            # Update the plot with fetched data
            self.update_plot()

            self.status_label.config(text="Status: Ready")
        except Exception as e:
            messagebox.showerror("Error", f"An error occurred:\n{e}")
            self.status_label.config(text="Status: Ready")

    def update_plot(self):
        if self.roads_gdf is None:
            return  # Nothing to plot yet

        self.ax.clear()

        # Plot buffer lines first so roads appear on top
        if self.show_buffers_var.get() and self.buffer_lines_gdf is not None:
            self.buffer_lines_gdf.plot(ax=self.ax, linewidth=2, edgecolor='red', label='Buffer Lines')

        # Plot roads network if enabled
        if self.show_roads_var.get():
            self.roads_gdf.plot(ax=self.ax, linewidth=1.5, edgecolor='blue', label='Roads')

        # Set plot title and legend
        town_name = self.entry.get().strip()
        self.ax.set_title(f"Road Network and Buffer for {town_name}", fontsize=16)
        self.ax.legend()

        # Adjust plot to fit the data
        self.ax.set_aspect('equal')
        self.ax.autoscale()
        self.ax.margins(x=0.01, y=0.01)  # Tighten the plot margins
        self.ax.axis('off')  # Hide axes for cleaner look

        self.fig.tight_layout()
        self.canvas.draw()

    def export_buffers(self):
        if self.buffer_lines_gdf is None:
            messagebox.showwarning("Export Warning", "No buffer lines to export.")
            return

        file_path = filedialog.asksaveasfilename(
            defaultextension=".shp",
            filetypes=[("Shapefile", "*.shp"), ("GeoJSON", "*.geojson")],
            title="Save Buffer Lines"
        )
        if file_path:
            try:
                self.buffer_lines_gdf.to_file(file_path)
                messagebox.showinfo("Export Success", f"Buffer lines exported to {file_path}")
            except Exception as e:
                messagebox.showerror("Export Error", f"Failed to export buffer lines:\n{e}")

def main():
    root = tk.Tk()
    root.geometry("1400x900")  # Increased window size for better visibility
    app = RoadNetworkApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()