<jupyter_start><jupyter_text>Geopandas and Shapefiles Here we can focus on the use of shapefiles through the tool of Geopandas.For this particular example we use the geological maps of Colombia.<jupyter_code>from shapely.geometry import shape, mapping, LineString, Polygon, Point
from shapely.ops import split
import shapely.speedups

import pandas as pd
import geopandas as gpd
import numpy as np
# Create geodataframe from shapefile
def loadSHPintoGeoDataFrame(SHPfilename,**kwargs):
    gdf = gpd.read_file(SHPfilename)
    print(gdf.crs) #sanity check
    Input_CRS = kwargs.get("crs","EPSG:4326")
    gdf = gdf.to_crs(Input_CRS) #Change projection to wgs84
    return gdf

# Create a new column on a geodataframe of the index from touching neighbors to ease processing
def gdf_GetNeighborsRows(df):
    shapely.speedups.enable() # Speed up spatial queries

    df = df.assign(IDXneighbors = "")
    for index, row in df.iterrows():  
        neighbors = df[df.geometry.touches(row['geometry'])].index.tolist()
        df.at[index, "IDXneighbors"] = neighbors
    return df

# Function to get the outside boundary of a polygon using shapely
def GetPolygonExterior(RowFromGDF):
    return shape(RowFromGDF['geometry']).boundary

# Use a given dictionary to fill top and bottom ages in a new column
def df_AgeEnrichment(df,FromColTop,FromColBottom,dictTop,dictBottom,ColumnAgeTop,ColumnAgeBottom):
    df[ColumnAgeTop] = df[FromColTop].apply(lambda x: dictTop.get(x, 9999.0) )
    df[ColumnAgeBottom] = df[FromColBottom].apply(lambda x: dictBottom.get(x, 9999.0))
    return df

PathTemp='./../SampleData/Colombia/'
shapefilename='Colombia.shp'
GeologyPlace="Geology_Colombia"
RowAgeName="edad"
nameofScript="Colombia Hiatus Maps"
dict_min_age = {'Serravaliano-Tortoniano': 5, 'Cenomaniano-Maastrichtiano': 66, 'Oligoceno-Mioceno': 5, 'Turoniano-Maastrichtiano': 66, 'Jur?sico': 145, 'Berriasiano-Valanginiano': 100, 'Barremiano-Albiano': 100, 'Tortoniano-Mesiniano': 5, 'Maastrichtiano-Paleoceno': 56, 'Campaniano-Paleoceno': 56, 'Rupeliano-Langhiano': 5, 'Bartoniano-Chatiano': 23, 'Cret?cico Inferior': 100, 'Aquitaniano-Burdigaliano': 5, 'Pos-Tri?sico': 0, 'Campaniano-Maastrichtiano': 66, 'Lutetiano-Bartoniano': 34, 'Cenomaniano-Santoniano': 66, 'Valanginiano-Cenomaniano': 66, 'Barremiano-Cenomaniano': 66, 'Plioceno': 3, 'Aquitaniano?-Tortoniano?': 5, 'Valanginiano-Albiano': 100, 'Burdigaliano-Langhiano': 5, 'Valanginiano': 100, 'Albiano-Campaniano': 66, 'Eoceno-Aquitaniano': 5, 'Chatiano-Aquitaniano': 5, 'Barremiano?-Albiano': 100, 'Berriasiano': 100, 'Valanginiano-Aptiano': 100, 'Cenomaniano?-Campaniano': 66, 'Berriasiano?-Barremiano': 100, 'Oligoceno': 23, 'Rupeliano-Burdigaliano': 5, 'Langhiano-Serravaliano': 5, 'Mioceno': 5, 'Albiano-Maastrichtiano': 66, 'Jur?sico Superior': 145, 'Tri?sico?': 201, 'Mesiniano-Zancleano': 3, 'Paleoceno-Eoceno': 34, 'Cret?cico Superior': 66, 'Berriasiano-Cenomaniano': 66, 'Valanginiano-Campaniano': 66, 'Paleoceno': 56, 'Thanetiano-Ypresiano': 34, 'Bartoniano-Priaboniano': 34, 'Albiano-Cenomaniano?': 66, 'Tri?sico Superior': 201, 'Aptiano?-Maastrichtiano': 66, 'Aptiano-Cenomaniano': 66, 'Jur?sico Inferior-Jur?sico Medio': 164, 'Tri?sico': 201, 'Cenomaniano?-Campaniano?': 66, 'Albiano': 100, 'Bartoniano-Rupeliano': 34, 'Jur?sico Medio-Jur?sico Superior': 145, 'Jur?sico Inferior': 174, 'Albiano-Turoniano': 66, 'Maastrichtiano': 66, 'Langhiano-Tortoniano': 5, 'Serravaliano-Mesiniano': 5, 'Aptiano-Maastrichtiano': 66, 'Eoceno': 34, 'Albiano-Cenomaniano': 66, 'Albiano?-Cenomaniano': 66, 'Berriasiano-Aptiano': 100, 'Aptiano-Albiano': 100}
dict_max_age = {'Serravaliano-Tortoniano': 23, 'Cenomaniano-Maastrichtiano': 100, 'Oligoceno-Mioceno': 34, 'Turoniano-Maastrichtiano': 100, 'Jur?sico': 201, 'Berriasiano-Valanginiano': 145, 'Barremiano-Albiano': 145, 'Tortoniano-Mesiniano': 23, 'Maastrichtiano-Paleoceno': 100, 'Campaniano-Paleoceno': 100, 'Rupeliano-Langhiano': 34, 'Bartoniano-Chatiano': 56, 'Plioceno-Pleistoceno': 5, 'Cret?cico Inferior': 145, 'Aquitaniano-Burdigaliano': 23, 'Pos-Tri?sico': 66, 'Campaniano-Maastrichtiano': 100, 'Lutetiano-Bartoniano': 56, 'Cenomaniano-Santoniano': 100, 'Valanginiano-Cenomaniano': 145, 'Barremiano-Cenomaniano': 145, 'Plioceno': 5, 'Aquitaniano?-Tortoniano?': 23, 'Cuaternario': 3, 'Valanginiano-Albiano': 145, 'Burdigaliano-Langhiano': 23, 'Valanginiano': 145, 'Albiano-Campaniano': 145, 'Eoceno-Aquitaniano': 56, 'Chatiano-Aquitaniano': 34, 'Barremiano?-Albiano': 145, 'Berriasiano': 145, 'Valanginiano-Aptiano': 145, 'Cenomaniano?-Campaniano': 100, 'Berriasiano?-Barremiano': 145, 'Oligoceno': 34, 'Rupeliano-Burdigaliano': 34, 'Langhiano-Serravaliano': 23, 'Mioceno': 23, 'Albiano-Maastrichtiano': 145, 'Jur?sico Superior': 164, 'Mesiniano-Zancleano': 23, 'Paleoceno-Eoceno': 66, 'Cret?cico Superior': 100, 'Berriasiano-Cenomaniano': 145, 'Valanginiano-Campaniano': 145, 'Paleoceno': 66, 'Thanetiano-Ypresiano': 66, 'Bartoniano-Priaboniano': 56, 'Albiano-Cenomaniano?': 145, 'Tri?sico Superior': 237, 'Aptiano?-Maastrichtiano': 145, 'Aptiano-Cenomaniano': 145, 'Jur?sico Inferior-Jur?sico Medio': 201, 'Cenomaniano?-Campaniano?': 100, 'Albiano': 145, 'Bartoniano-Rupeliano': 34, 'Jur?sico Medio-Jur?sico Superior': 174, 'Jur?sico Inferior': 201, 'Albiano-Turoniano': 145, 'Pleistoceno': 3, 'Maastrichtiano': 100, 'Langhiano-Tortoniano': 23, 'Serravaliano-Mesiniano': 23, 'Aptiano-Maastrichtiano': 145, 'Eoceno': 56, 'Albiano-Cenomaniano': 145, 'Albiano?-Cenomaniano': 145, 'Berriasiano-Aptiano': 145, 'Aptiano-Albiano': 145}
MapGDF = loadSHPintoGeoDataFrame(PathTemp+shapefilename) #load of shp map + reprojection to wgs84

#Check if more than one column name. If 2 columns is the case, then first name is bottom, second is top
if (isinstance(RowAgeName, tuple)):
    MapGDF = df_AgeEnrichment(MapGDF, RowAgeName[0], RowAgeName[1], dict_min_age, dict_max_age, "ChronoAgeTop", "ChronoAgeBottom")
elif (isinstance(RowAgeName, str)):
    MapGDF = df_AgeEnrichment(MapGDF, RowAgeName, RowAgeName, dict_min_age, dict_max_age, "ChronoAgeTop", "ChronoAgeBottom")
MapGDF = gdf_GetNeighborsRows(MapGDF) #get a new column for neighboring locations' index
indx = 5558
row = MapGDF.iloc[indx]
df2 = MapGDF.iloc[row["IDXneighbors"]]
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
ax = world[world.name == 'Colombia'].plot( figsize=(5, 5),
    color='white', edgecolor='black')
df2.plot(ax=ax, color='red')

MapGDF.loc[[indx],'geometry'].plot(ax=ax, color='blue')

ax2 = df2.plot( figsize=(5, 5),
    color='red')
MapGDF.loc[[indx],'geometry'].plot(ax=ax2, color='blue')
MapGDF.loc[[indx]]<jupyter_output><empty_output><jupyter_text>Data enrichment of a set of points<jupyter_code>MyPoints = pd.DataFrame(
    {'Pin': ['A', 'B', 'C', 'D', 'E'],
     'Latitude': [4.95, 4.95, 4.95, 4.95, 4.95],
     'Longitude': [-73.9, -73.88, -73.86, -73.84, -73.82]})

gdf = gpd.GeoDataFrame(
    MyPoints, geometry=gpd.points_from_xy(MyPoints.Longitude, MyPoints.Latitude), 
    crs="EPSG:4326")
ax3 = df2.plot(color='red', figsize=(5, 5))
MapGDF.loc[[indx],'geometry'].plot(ax=ax3, color='lightblue')
gdf.plot(ax=ax3,marker="x",color="black")
gdf
gpd.sjoin(MapGDF,gdf,how="right")[["Pin","Latitude","Longitude","simbolo_uc","ChronoAgeTop","ChronoAgeBottom"]]<jupyter_output><empty_output><jupyter_text>Find unique tops<jupyter_code>ColumnAgeTop = "ChronoAgeTop"; ColumnAgeBottom = "ChronoAgeBottom"

BottomAgesList = MapGDF[ColumnAgeBottom].unique().tolist()
BottomAgesList = [x for x in BottomAgesList if x != 9999.0]

UnderlyingUnits = MapGDF[MapGDF.ChronoAgeTop.isin(BottomAgesList)]

tmp_list = []
for index, Row in UnderlyingUnits.iterrows():
    tmp_list.append({
        'Age': Row[ColumnAgeTop],
        'Value': -200.0,
        'geometry' : GetPolygonExterior(Row),
         })
    
ConfLinesDF = pd.DataFrame(tmp_list)
ConfLinesGDF = gpd.GeoDataFrame(ConfLinesDF, crs="EPSG:4326", geometry = ConfLinesDF.geometry)
ConfLinesGDF.plot(figsize=(5, 5))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.15997498436522828, 'fraction_numerical': 0.051407129455909945, 'mean_word_length': 6.778210116731517, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '452991', 'license': 'MIT', 'n_tokens_mistral': 3600, 'n_tokens_neox': 3130, 'n_words': 627, 'path': 'notebooks/02-GeopandasAndSHP.ipynb', 'repo_name': 'Nicolucas/PyTutorial-PANDAS', 'size': '7995'}