-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLOD.py
281 lines (255 loc) · 9.31 KB
/
LOD.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
"""
Created on Fri Feb 03 12:04:07 2017
@author: ASAD
--------------------------------------------------------------------------------------
This code uses GDAL and OGR to generate LOD1 from DSM and DTM data.
--------------------------------------------------------------------------------------
"""
from osgeo import gdal,ogr
import numpy as np
import os
#%%
'''
Getting data filenames from specified path
'''
def filenames(path):
"""
Returns all filenames in specified directory.
Input arguments:
path = Name of the folder or directory from which filenames are to be read
Usage:
names = filenames('drive:/directory')
"""
return os.listdir(path) # To get name of all the files stored in given directory
#%%
'''
Reading Raster
'''
def readRaster(path):
"""
Reads raster from specified path and returns raster information.
Input arguments:
path = complete path of raster to be read including its name
Returns:
col = Number of raster columns
row = Number of raster rows
bandNum = Number of raster bands
band = Raster band
dataRaster = Raster band as array
geotansform = Raster geometric transformation information
Usage:
col,row,bandNum,band,dataRaster,geotransform = readRaster('drive:/directory/raster')
"""
raster = gdal.Open(path) # opening Raster
col = raster.RasterXSize # getting number of columns
row = raster.RasterYSize # getting number of rows
bandNum= raster.RasterCount # counting number of bands in a raster
geotransform = raster.GetGeoTransform()
# originX = geotransform[0]
# originY = geotransform[3]
# pixelWidth = geotransform[1]
# pixelHeight = geotransform[5]
band=raster.GetRasterBand(1) # getting 1st raster band
dataRaster=band.ReadAsArray(0, 0, col, row) # reading raster as array
print raster.GetMetadata() # printing meta data
return (col,row,bandNum,band,dataRaster,geotransform)
#%%
'''
Removing no data values and creating BnDSM
'''
def bAndnDSM(DSM,noDataValDSM,DTM,noDataValDTM,binThreshold):
"""
Creates BnDSM and nDSM from DSM and DTM.
Input arguments:
DSM = DSM raster array
noDataValDSM = No data value of DSM
DTM = DTM raster array.
noDataValDTM = No data value of DTM
binThreshold = Height threshold to get BnDSM.
Returns:
nDSM = Normalized DSM
BnDSM = Binary nDSM
Usage:
nDSM,BnDSM = bAndnDSM(DSM,noDataValDSM,DTM,noDataValDTM,binThreshold)
"""
DSM=np.where(DSM>=noDataValDSM,np.nan,DSM) # replacing nodata with NAN
DTM=np.where(DTM>=noDataValDTM,np.nan,DTM) # replacing nodata with NAN
nDSM=DSM-DTM# Calculating nDSM
BnDSM=np.greater(nDSM,binThreshold)
return (nDSM,BnDSM)# Creating a binary raster by using height threshold on nDSM.
#%%
'''
Writing Raster
'''
def writeRaster(location,Value,cols,rows):
"""
Writes the raster to specified location.
Input arguments:
location = complete path with name of output raster
Value = raster array
cols = number of raster columns
rows = number of raster rows
Usage:
writeRaster(location,Value,cols,rows)
"""
driver = Value.GetDriver() # Getting same driver as DSM
outDataset = driver.Create(location,cols,rows,1)#Creating Output Dataset
outBand = outDataset.GetRasterBand(1) # Specifying band to which data is written
geoTransform = Value.GetGeoTransform()#getting geometric transformation information from dsm
outDataset.SetGeoTransform(geoTransform)
outBand.WriteArray(Value,0,0) # Writing binary raster to specified file.
outBand =None # cleaning up the memory
outDataset=None # cleaning up the memory
#%%
def writeLOD1(location,dataBand,maskBand,fieldName):
"""
Writes LOD1 to specified path
Input arugments:
location = complete output location of LOD1 shapefile with name
dataBand = nDSM band (This should not be array)
maskBand = BnDSM band (This should not be array)
fieldName = fieldName to which elevation data is written.
Usage:
writeLOD1(location,dataBand,maskBand,fieldName)
"""
dst_layername = location
drv = ogr.GetDriverByName("ESRI Shapefile")
dst_ds = drv.CreateDataSource( dst_layername + ".shp")
dst_layer = dst_ds.CreateLayer(dst_layername,geom_type=ogr.wkbPolygon)
# Creating new field
fd = ogr.FieldDefn(fieldName,ogr.OFTInteger)
dst_layer.CreateField(fd)
# Polygonize
gdal.Polygonize(dataBand,maskBand, dst_layer,0)
# writing feature to shapefile
featureDefn = dst_layer.GetLayerDefn()
feature = ogr.Feature(featureDefn)
dst_layer.CreateFeature(feature)
feature.Destroy()
dst_ds.Destroy()
#%%
def PolygonizeBnDSM(BnDSM,OutputLocation):
"""
To extract building foot prints using BnDSM.
Input arguments:
BnDSM = Binary nDSM can be obtained by bAndnDSM() function
OutputLocation = Complete path of output file including filename
"""
maskBand=BnDSM
dst_layername = OutputLocation
drv = ogr.GetDriverByName("ESRI Shapefile")
dst_ds = drv.CreateDataSource( dst_layername + ".shp" ,)
dst_layer = dst_ds.CreateLayer(dst_layername,geom_type=ogr.wkbPolygon)
gdal.Polygonize(maskBand,maskBand, dst_layer,0)
featureDefn = dst_layer.GetLayerDefn()
feature = ogr.Feature(featureDefn)
dst_layer.CreateFeature(feature)
feature.Destroy()
dst_ds.Destroy()
#%%
"""
Zonal Statistics
Vector-Raster Analysis
Copyright 2013 Matthew Perry
Usage:
zonal_stats.py VECTOR RASTER
zonal_stats.py -h | --help
zonal_stats.py --version
Options:
-h --help Show this screen.
--version Show version.
"""
gdal.PushErrorHandler('CPLQuietErrorHandler')
def bbox_to_pixel_offsets(gt, bbox):
originX = gt[0]
originY = gt[3]
pixel_width = gt[1]
pixel_height = gt[5]
x1 = int((bbox[0] - originX) / pixel_width)
x2 = int((bbox[1] - originX) / pixel_width) + 1
y1 = int((bbox[3] - originY) / pixel_height)
y2 = int((bbox[2] - originY) / pixel_height) + 1
xsize = x2 - x1
ysize = y2 - y1
return (x1, y1, xsize, ysize)
def zonal_stats(vector_path, raster_path, nodata_value=None, global_src_extent=False):
rds = gdal.Open(raster_path)
assert(rds)
rb = rds.GetRasterBand(1)
rgt = rds.GetGeoTransform()
if nodata_value:
nodata_value = float(nodata_value)
rb.SetNoDataValue(nodata_value)
vds = ogr.Open(vector_path) # TODO maybe open update if we want to write stats
assert(vds)
vlyr = vds.GetLayer(0)
# create an in-memory numpy array of the source raster data
# covering the whole extent of the vector layer
if global_src_extent:
# use global source extent
# useful only when disk IO or raster scanning inefficiencies are your limiting factor
# advantage: reads raster data in one pass
# disadvantage: large vector extents may have big memory requirements
src_offset = bbox_to_pixel_offsets(rgt, vlyr.GetExtent())
src_array = rb.ReadAsArray(*src_offset)
# calculate new geotransform of the layer subset
new_gt = (
(rgt[0] + (src_offset[0] * rgt[1])),
rgt[1],
0.0,
(rgt[3] + (src_offset[1] * rgt[5])),
0.0,
rgt[5]
)
mem_drv = ogr.GetDriverByName('Memory')
driver = gdal.GetDriverByName('MEM')
# Loop through vectors
stats = []
feat = vlyr.GetNextFeature()
while feat is not None:
if not global_src_extent:
# use local source extent
# fastest option when you have fast disks and well indexed raster (ie tiled Geotiff)
# advantage: each feature uses the smallest raster chunk
# disadvantage: lots of reads on the source raster
src_offset = bbox_to_pixel_offsets(rgt, feat.geometry().GetEnvelope())
src_array = rb.ReadAsArray(*src_offset)
# calculate new geotransform of the feature subset
new_gt = (
(rgt[0] + (src_offset[0] * rgt[1])),
rgt[1],
0.0,
(rgt[3] + (src_offset[1] * rgt[5])),
0.0,
rgt[5]
)
# Create a temporary vector layer in memory
mem_ds = mem_drv.CreateDataSource('out')
mem_layer = mem_ds.CreateLayer('poly', None, ogr.wkbPolygon)
mem_layer.CreateFeature(feat.Clone())
# Rasterize it
rvds = driver.Create('', src_offset[2], src_offset[3], 1, gdal.GDT_Byte)
rvds.SetGeoTransform(new_gt)
gdal.RasterizeLayer(rvds, [1], mem_layer, burn_values=[1])
rv_array = rvds.ReadAsArray()
# Mask the source data array with our current feature
# we take the logical_not to flip 0<->1 to get the correct mask effect
# we also mask out nodata values explictly
masked = np.ma.MaskedArray(
src_array,
mask=np.logical_or(
src_array == nodata_value,
np.logical_not(rv_array)
)
)
feature_stats = {
'mean': float(masked.mean()),
'count': int(masked.count()),
'fid': int(feat.GetFID())}
stats.append(feature_stats)
rvds = None
mem_ds = None
feat = vlyr.GetNextFeature()
vds = None
rds = None
return stats