DEMO 3-2: Geospatial Coordinate Conversion and Shapefile Generator App

import geopandas as gpd
from shapely.geometry import box
from pathlib import Path
from shapely.geometry import Polygon
from pyproj import CRS
import zipfile
import os
import gradio as gr

# Define the start and end points in decimal degrees and DMS format
start_point =["109°35'57'',E", "41°50'49'',N"]  
end_point = ["110°12'24'',E","41°43'16'',N"]

start_point = ["109.57,E", "41.59,N"]  
end_point = ["110.12,E","41.16,N"]

# Function to convert decimal degrees to degrees, minutes, and seconds (DMS)
def deg_to_dms(coord):
    deg, direction = coord.split(',')
    deg = float(deg)
    sign = 1 if direction in ['N', 'E'] else -1
    deg_abs = abs(deg)
    deg_int = int(deg_abs)*sign
    minutes = (deg_abs - deg_int) * 60
    seconds = (minutes - int(minutes)) * 60
    return f"{deg_int}° {int(minutes)}' {int(seconds)}'',{direction}",deg

# Function to convert degrees, minutes, and seconds (DMS) to decimal degrees
def dms_to_deg(coord):
    parts = coord.replace('°', "'").replace("''", "'").replace(",", "'").split("'")
    deg = float(parts[0])
    minutes = float(parts[1])
    seconds = float(parts[2])
    direction = parts[-1]
    decimal_deg = deg + (minutes / 60) + (seconds / 3600)
    sign = 1 if direction in ['N', 'E'] else -1
    decimal_deg = decimal_deg*sign
    return f"{decimal_deg:.4f},{direction}",decimal_deg

# Coordinates converter
def coord_convert(start_pointx,start_pointy,end_pointx,end_pointy,dms='True'):
    # print(dms)
    if dms=='True':
        textlon_min,lon_min=dms_to_deg(start_pointx)
        textlat_max,lat_max=dms_to_deg(start_pointy)
        textlon_max,lon_max=dms_to_deg(end_pointx)
        textlat_min,lat_min=dms_to_deg(end_pointy) 
    else:
        textlon_min,lon_min=deg_to_dms(start_pointx)
        textlat_max,lat_max=deg_to_dms(start_pointy)
        textlon_max,lon_max=deg_to_dms(end_pointx)
        textlat_min,lat_min=deg_to_dms(end_pointy)
    start_point=lon_min,lat_max
    end_point=lon_max,lat_min
    textstart_point=textlon_min,textlat_min
    textend_point=textlon_max,textlat_max
    return  textstart_point,textend_point,start_point,end_point

# Function to compress a Shapefile into a .zip file for easy downloading
def shp_to_zip(shp_file):
    directory = os.path.dirname(shp_file)
    basename = os.path.splitext(os.path.basename(shp_file))[0]
    extensions = ['.dbf', '.shx', '.cpg', '.prj','.shp']
    zip_filename = os.path.join(directory, f"{basename}.zip")
    with zipfile.ZipFile(zip_filename, 'w') as zipf:
        for ext in extensions:
            filename = os.path.join(directory, f"{basename}{ext}")
            if os.path.exists(filename):
                zipf.write(filename, arcname=f"{basename}{ext}")
    # print(zip_filename)
    return zip_filename

# Function to generate a Shapefile from the converted coordinates
def generate_shp(start_point,end_point):
    print(start_point)
    print(end_point)
    vertices = [(start_point[0], start_point[1]), (start_point[0], end_point[1]),(end_point[0], end_point[1]),(end_point[0], start_point[1])]
    polygon = Polygon(vertices)
    gdf = gpd.GeoDataFrame(geometry=[polygon], crs=CRS('EPSG:4326').to_wkt())
    shpfile='./result.shp'
    gdf.to_file(shpfile)
    tempfile=shp_to_zip(shpfile)
    return gr.DownloadButton(label=f"Download {Path(tempfile).stem}", value=tempfile, visible=True)

with gr.Blocks(theme='NoCrypt/Miku') as demo:
    start_point=gr.State(None) 
    end_point=gr.State(None) 
    type=gr.State('False')
    with gr.Tab("Convert dms coord to shp"): # dms coords to shp
        with gr.Row():
            start_pointx=gr.Textbox(value="115°25'00'',E",label="Longtitude (top left)",interactive=True)
            start_pointy=gr.Textbox(value="41°03'00'',N",label="Latitude (top left)",interactive=True)
            end_pointx=gr.Textbox(value="117°30'00'',E",label="Longtitude (down left)",interactive=True)
            end_pointy=gr.Textbox(value="39°26'00'',N",label="Latitude(down left)",interactive=True)
        with gr.Row():
            convert_start=gr.Textbox(placeholder='',label="converted top left point")
            convert_end=gr.Textbox(placeholder='',label="converted top left point")
        with gr.Row():
            read_button=gr.Button("Read the corrd",visible=True,variant='primary')
            runbutton=gr.Button("Generate SHP",variant='primary')
            image_button=gr.DownloadButton("Download",visible=True,variant='secondary')
                    
        read_button.click(coord_convert,[start_pointx,start_pointy,end_pointx,end_pointy],[convert_start,convert_end,start_point,end_point])
        runbutton.click(generate_shp, [start_point,end_point],image_button)   
        
    with gr.Tab("Convert degree coord to shp"): # deg coords to shp
        with gr.Row():
            start_pointx=gr.Textbox(value="115.42,E",label="Longtitude (top left)",interactive=True)
            start_pointy=gr.Textbox(value="41.05,N",label="Latitude (top left)",interactive=True)
            end_pointx=gr.Textbox(value="117.50,E",label="Longtitude (down left)",interactive=True)
            end_pointy=gr.Textbox(value="39.43,N",label="Latitude(down left)",interactive=True)
        with gr.Row():
            convert_start=gr.Textbox(placeholder='',label="converted top left point")
            convert_end=gr.Textbox(placeholder='',label="converted top left point")
        with gr.Row():
            read_button=gr.Button("Read the corrd",visible=True,variant='primary')
            runbutton=gr.Button("Generate SHP",variant='primary')
            image_button=gr.DownloadButton("Download",visible=True,variant='secondary')
            
        read_button.click(coord_convert,[start_pointx,start_pointy,end_pointx,end_pointy,type],[convert_start,convert_end,start_point,end_point])
        runbutton.click(generate_shp, [start_point,end_point],image_button)  

demo.launch()