Como crear un Raster de Elevación de Líneas de Contorno con Python, Geopandas, Numpy y GDAL

createRasterwithPython.PNG

El análisis espacial es una disciplina muy interesante porque permite la evaluación de todos los fenómenos relacionados con su ubicación. Sin embargo, para algunas partes del procesamiento de datos, el flujo de trabajo en una interfaz gráfica de computadora (GUI) para SIG puede ser repetitivo y llevar mucho tiempo. Los investigadores necesitan herramientas mejores y más eficientes para procesar más cantidad de datos en menos tiempo e incluso con menos cantidad de herramientas de software.

Hemos creado un script innovador para generar un archivo ráster de elevación desde un líneas de contorno con varios pasos de procesamiento de datos. El script reconoce geometrías inválidas, simplifica las polilíneas y extrae vértices mientras crea un geodataframe de puntos que se interpola y se geotransforma como un ráster geoespacial en formato .tiff.

Tutorial

Código en Python


Geopandas geodataframes generation

%matplotlib inline
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
from shapely.geometry import Point, Polygon
from shapely.ops import split

#Shapefile list
%ls ..\Shp\*.shp
El volumen de la unidad C no tiene etiqueta.
 El n£mero de serie del volumen es: 24E6-96EB

 Directorio de C:\Users\GIDAWK1\Documents\howToCreateAreaofInterestRasterfromContourLineswithPythonandGeopandas\Shp

27/11/2019  09:43 a.m.         7,667,184 contours_10m.shp
29/11/2019  10:51 a.m.         7,436,972 contours_10m_v2.shp
02/12/2019  03:08 p.m.           187,336 pointsAoi.shp
02/12/2019  11:44 a.m.               236 rasterAoi.shp
               4 archivos     15,291,728 bytes
               0 dirs  28,501,250,048 bytes libres
#Open countour lines and area of interest
topo1 = gpd.read_file('../Shp/contours_10m_v2.shp')
rasterAoi = gpd.read_file('../Shp/rasterAoi.shp')
aoiGeom = rasterAoi.loc[0].geometry
topo1.crs
{'init': 'epsg:32611'}
#Plot of the countour line with the selected area to extract vertices
ax = rasterAoi.plot(color='green', figsize=(12,12), alpha=0.2);
topo1.plot(ax=ax,alpha=0.5)
ax.grid()
output_3_0.png

Vertex extraction to the Area of Interest

compDf = gpd.GeoDataFrame()
compDf['geometry']=None
i=0

for index, row in topo1.iterrows():
    #index counter
    if index % 100 == 0:
        print('Processing polylines_'+str(index)+'\n')

    if row.geometry!=None: #Check for false geometries
        try:          
            if row.geometry.disjoint(aoiGeom)==False: #Filter all lines that dont cross the polygon 
                #print(len(row.geometry.coords))
                simpRow = row.geometry.simplify(5,preserve_topology=True)
                vertexBefore = len(row.geometry.coords)
                vertexAfter = len(simpRow.coords)
                reductionPercen = vertexAfter/vertexBefore*100

                print('Vertex before: %d, Vertex after: %d, Reduction rate: %.2f'%(vertexBefore,vertexAfter,reductionPercen))
                for coord in simpRow.coords:
                    if Point(coord).within(aoiGeom): #Check if vertex is inside the polygon
                        compDf.loc[i]=str(row.ID) #Assign values to the dataframe
                        compDf.loc[i,'Easting']=coord[0]
                        compDf.loc[i,'Northing']=coord[1]
                        compDf.loc[i,'Elevation']=row.CONTOUR
                        compDf.loc[i,'geometry']=Point(coord)
                        i+=1

                        #Counter of proccessed points
                        if i % 1000 == 0:
                            print('Processed Vertices_'+str(i))                    
        except ValueError: pass

compDf.head()
Processing polylines_0

Vertex before: 81, Vertex after: 48, Reduction rate: 59.26
Vertex before: 1888, Vertex after: 1035, Reduction rate: 54.82
Vertex before: 2111, Vertex after: 1049, Reduction rate: 49.69
Vertex before: 2486, Vertex after: 1229, Reduction rate: 49.44
Vertex before: 2665, Vertex after: 1291, Reduction rate: 48.44
Processed Vertices_1000
Vertex before: 2721, Vertex after: 1288, Reduction rate: 47.34
Vertex before: 2828, Vertex after: 1286, Reduction rate: 45.47
Vertex before: 3056, Vertex after: 1306, Reduction rate: 42.74
Processed Vertices_2000
Vertex before: 3093, Vertex after: 1324, Reduction rate: 42.81
Vertex before: 3480, Vertex after: 1475, Reduction rate: 42.39
Vertex before: 3893, Vertex after: 1655, Reduction rate: 42.51
Processed Vertices_3000
Vertex before: 3111, Vertex after: 1275, Reduction rate: 40.98
Vertex before: 3654, Vertex after: 1398, Reduction rate: 38.26
Vertex before: 3558, Vertex after: 1216, Reduction rate: 34.18
Vertex before: 3612, Vertex after: 1294, Reduction rate: 35.83
Processed Vertices_4000
Vertex before: 3606, Vertex after: 1283, Reduction rate: 35.58
Vertex before: 3674, Vertex after: 1226, Reduction rate: 33.37
Vertex before: 3723, Vertex after: 1268, Reduction rate: 34.06
Vertex before: 3796, Vertex after: 1290, Reduction rate: 33.98
Processed Vertices_5000
Vertex before: 3832, Vertex after: 1293, Reduction rate: 33.74
Vertex before: 3838, Vertex after: 1207, Reduction rate: 31.45
Vertex before: 4043, Vertex after: 1247, Reduction rate: 30.84
Vertex before: 4121, Vertex after: 1259, Reduction rate: 30.55
Vertex before: 4177, Vertex after: 1252, Reduction rate: 29.97
Processed Vertices_6000
Vertex before: 4293, Vertex after: 1245, Reduction rate: 29.00
Vertex before: 4399, Vertex after: 1310, Reduction rate: 29.78
Vertex before: 4367, Vertex after: 1242, Reduction rate: 28.44
Vertex before: 4435, Vertex after: 1259, Reduction rate: 28.39
Vertex before: 4490, Vertex after: 1266, Reduction rate: 28.20
Vertex before: 4525, Vertex after: 1316, Reduction rate: 29.08
Vertex before: 4616, Vertex after: 1367, Reduction rate: 29.61
Vertex before: 4679, Vertex after: 1349, Reduction rate: 28.83
Vertex before: 4857, Vertex after: 1445, Reduction rate: 29.75
Vertex before: 4945, Vertex after: 1489, Reduction rate: 30.11
Vertex before: 5242, Vertex after: 1620, Reduction rate: 30.90
Vertex before: 5754, Vertex after: 1666, Reduction rate: 28.95
Vertex before: 6588, Vertex after: 1929, Reduction rate: 29.28
Processing polylines_100

Processing polylines_200

Processing polylines_300
geometry Easting Northing Elevation
0 POINT (222455.383 3788700.041) 222455 3.7887e+06 -590
1 POINT (222411.465 3788720.048) 222411 3.78872e+06 -590
2 POINT (222401.342 3788809.926) 222401 3.78881e+06 -590
3 POINT (222355.383 3788845.643) 222355 3.78885e+06 -590
4 POINT (222255.383 3788803.316) 222255 3.7888e+06 -590
#Representation of the extracted vertices and the area of interest
ax = rasterAoi.plot(color='green', figsize=(12,12), alpha=0.2);
topo1.plot(ax=ax,color='cyan',alpha=0.5)
compDf.plot(ax=ax,alpha=0.5)
ax.grid()
output_6_0.png
#Export point to shapefile
compDf.crs={'init': 'epsg:32611'}
compDf.to_file('../Shp/pointsAoi.shp')

Point interpolation

import numpy as np
from scipy.interpolate import griddata

#From the dataFrame
points = compDf[['Easting','Northing']].values
values = compDf['Elevation'].values

#Assign the raster resoulution
rasterRes = 100.0

#Get the Area of Interest dimensions and number of rows / cols
print(aoiGeom.bounds)
xDim = aoiGeom.bounds[2]-aoiGeom.bounds[0]
yDim = aoiGeom.bounds[3]-aoiGeom.bounds[1]
print('Raster X Dim: %.2f, Raster Y Dim: %.2f'%(xDim,yDim))
nCols = xDim / rasterRes
nRows = yDim / rasterRes
print('Number of cols:  %.2f, Number of rows: %.2f'%(nCols,nRows)) #Check if the cols and row don't have decimals
(222000.0, 3783000.0, 243000.0, 3796000.0)
Raster X Dim: 21000.00, Raster Y Dim: 13000.00
Number of cols:  210.00, Number of rows: 130.00
#We create an array on the cell centroid
grid_y, grid_x = np.mgrid[aoiGeom.bounds[1]+rasterRes/2:aoiGeom.bounds[3]-rasterRes/2:nRows*1j,
                         aoiGeom.bounds[0]+rasterRes/2:aoiGeom.bounds[2]+rasterRes/2:nCols*1j]

mtop = griddata(points, values, (grid_x, grid_y), method='cubic')
mtop[:5,:5]
array([[          nan, -480.77331   , -480.06521355, -479.63126685,
        -480.03963447],
       [          nan, -485.85168813, -484.67218837, -484.55962848,
        -483.67878073],
       [          nan, -491.72186276, -490.12375354, -491.89022442,
        -491.8011287 ],
       [          nan, -498.59717162, -498.41920838, -497.27970783,
        -497.03388778],
       [          nan, -504.09576321, -504.01128831, -501.76031806,
        -500.74936978]])
import matplotlib.pyplot as plt
plt.imshow(mtop)
plt.colorbar()
<matplotlib.colorbar.Colorbar at 0xeaaafd0>
output_11_1.png

Raster generation

from osgeo import gdal, gdal_array, osr

geotransform = [aoiGeom.bounds[0],rasterRes,0,aoiGeom.bounds[1],0,rasterRes] #[xmin,xres,0,ymax,0,-yres]
raster = gdal.GetDriverByName('GTiff').Create("../Rst/aoiRaster.tif",int(nCols),int(nRows),1,gdal.GDT_Float64)
raster.SetGeoTransform(geotransform)

srs=osr.SpatialReference()
srs.ImportFromEPSG(32611)

raster.SetProjection(srs.ExportToWkt())
raster.GetRasterBand(1).WriteArray(mtop)
del raster

Datos de entrada

Puedes descargar los datos de entrada de este enlace.

 

Suscríbete a nuestro boletín electrónico

Suscríbase a nuestro boletín gratuito para recibir noticias, datos interesantes y fechas de nuestros cursos en recursos hídricos.

 

Posted on December 2, 2019 and filed under TutorialQGIS, TutorialPython.