Skip to content

Commit 6a1851c

Browse files
committed
zfrac
1 parent 9776bfb commit 6a1851c

File tree

2 files changed

+69
-42
lines changed

2 files changed

+69
-42
lines changed

geospatial_learn/shape.py

+63-40
Original file line numberDiff line numberDiff line change
@@ -942,20 +942,20 @@ def zonal_stats(inShp, inRas, band, bandname, layer=None, stat = 'mean',
942942
src_offset = _bbox_to_pixel_offsets(rgt, geom)
943943

944944
#x, y, xcount, ycount - so if counts are 0, the geom is smaller than
945-
# the pixel, so we can give it a shape of 1x1
945+
# the pixel, so we can give it a shape of 1xninS
946946
if src_offset[2] == 0:
947947
src_offset[2] = 1
948948
if src_offset[3] == 0:
949949
src_offset[3] = 1
950950

951951
# This does not seem to be fullproof
952952
# This is a hacky mess that needs fixed
953-
if poly.Contains(geom) == False:
954-
#print(src_offset[0],src_offset[1])
955-
#offs.append()
956-
feat = vlyr.GetNextFeature()
957-
continue
958-
elif src_offset[0] > rds.RasterXSize:
953+
# if poly.Contains(geom) == False: # this isn't working -
954+
# #print(src_offset[0],src_offset[1])
955+
# #offs.append()
956+
# feat = vlyr.GetNextFeature()
957+
# continue
958+
if src_offset[0] > rds.RasterXSize:
959959
feat = vlyr.GetNextFeature()
960960
continue
961961
elif src_offset[1] > rds.RasterYSize:
@@ -1021,7 +1021,7 @@ def zonal_stats(inShp, inRas, band, bandname, layer=None, stat = 'mean',
10211021
)
10221022

10231023
if stat == 'mode':
1024-
feature_stats = mode(masked)[0]
1024+
feature_stats = mode(masked, axis=None)[0][0]
10251025
elif stat == 'min':
10261026
feature_stats = float(masked.min())
10271027
elif stat == 'mean':
@@ -1055,7 +1055,7 @@ def zonal_stats(inShp, inRas, band, bandname, layer=None, stat = 'mean',
10551055
feature_stats = float(cellvol.sum())
10561056
else:
10571057
raise ValueError("Must be one of mode, min, mean, max,"
1058-
"std, sum, count, var, skew, kurt, vol")
1058+
"std, sum, count, var, skew, kurt, vol, mode")
10591059
# You can't have the stat of a single value - this is not an ideal
10601060
# solution - should be flagged somehow but
10611061
if src_array.shape == (1,1):
@@ -1074,9 +1074,9 @@ def zonal_stats(inShp, inRas, band, bandname, layer=None, stat = 'mean',
10741074

10751075
vds = None
10761076
rds = None
1077-
frame = DataFrame(stats)
1077+
frame = DataFrame(data=stats, columns=[stat])
10781078

1079-
if write_stat != None:
1079+
if write_stat is None:
10801080
return frame, rejects
10811081

10821082
def zonal_frac(inShp, inRas, band, bandname, layer=None,
@@ -1151,11 +1151,11 @@ def zonal_frac(inShp, inRas, band, bandname, layer=None,
11511151
if write_stat != None:
11521152
# if the field exists leave it as ogr is a pain with dropping it
11531153
# plus can break the file
1154-
if _fieldexist(vlyr, bandname+'_cls') == False:
1155-
vlyr.CreateField(ogr.FieldDefn(bandname+'_cls', ogr.OFTIntegerList))
1154+
# intlist not used see later code #ogr.OFTIntegerList))
1155+
if _fieldexist(vlyr, bandname+'_cls') == False:
1156+
vlyr.CreateField(ogr.FieldDefn(bandname+'_cls', ogr.OFTString))
11561157
if _fieldexist(vlyr, bandname+'_cnt') == False:
1157-
vlyr.CreateField(ogr.FieldDefn(bandname+'_cnt', ogr.OFTIntegerList))
1158-
1158+
vlyr.CreateField(ogr.FieldDefn(bandname+'_cnt', ogr.OFTString))
11591159

11601160
mem_drv = ogr.GetDriverByName('Memory')
11611161
driver = gdal.GetDriverByName('MEM')
@@ -1167,8 +1167,17 @@ def zonal_frac(inShp, inRas, band, bandname, layer=None,
11671167
rejects = []
11681168

11691169
#create a poly of raster bbox to test for within raster
1170+
# this is
11701171
poly = rasterext2poly(inRas)
11711172

1173+
# so we can map the field name to an integer when setting the list
1174+
# (yes I know....)
1175+
fieldict = {}
1176+
ldefn = vlyr.GetLayerDefn()
1177+
for n in range(ldefn.GetFieldCount()):
1178+
fdefn = ldefn.GetFieldDefn(n)
1179+
fieldict.update({fdefn.name:n})
1180+
11721181
#TODO FAR too many if statements in this loop.
11731182
# This is FAR too slow
11741183

@@ -1191,12 +1200,12 @@ def zonal_frac(inShp, inRas, band, bandname, layer=None,
11911200

11921201
# This does not seem to be fullproof
11931202
# This is a hacky mess that needs fixed
1194-
if poly.Contains(geom) == False:
1195-
#print(src_offset[0],src_offset[1])
1196-
#offs.append()
1197-
feat = vlyr.GetNextFeature()
1198-
continue
1199-
elif src_offset[0] > rds.RasterXSize:
1203+
# if poly.Contains(geom) == False: - not working rejects legit polygons
1204+
# #print(src_offset[0],src_offset[1])
1205+
# #offs.append()
1206+
# feat = vlyr.GetNextFeature()
1207+
# continue
1208+
if src_offset[0] > rds.RasterXSize:
12001209
feat = vlyr.GetNextFeature()
12011210
continue
12021211
elif src_offset[1] > rds.RasterYSize:
@@ -1250,29 +1259,42 @@ def zonal_frac(inShp, inRas, band, bandname, layer=None,
12501259
gdal.RasterizeLayer(rvds, [1], mem_layer, burn_values=[1], options=[touch])
12511260
rv_array = rvds.ReadAsArray()
12521261

1253-
# Mask the source data array with our current feature using np mask
1254-
1255-
#rejects.append(feat.GetField('DN'))
1256-
masked = np.ma.MaskedArray(
1257-
src_array,
1258-
mask=np.logical_or(
1259-
src_array == nodata_value,
1260-
np.logical_not(rv_array)
1261-
)
1262-
)
1263-
unique, count = np.unique(masked.data, return_counts=True)
1262+
# masked array dumped as it causes problems with unique producing non-unique
1263+
# arrays - also slows it down
1264+
if src_array.shape == (1,1):
1265+
unique = src_array[0]
1266+
count = np.array([1])
1267+
else:
1268+
unique, count = np.unique(src_array[rv_array>0], return_counts=True)
12641269

1265-
stats.append([feat.GetFID(), unique, count])
1270+
entry = [feat.GetFID(), unique.tolist(), count.tolist()]
1271+
1272+
stats.append(entry)
12661273

12671274
if write_stat != None:
1268-
# may have to insert into gdf as array
1269-
# TypeError: Feature_SetFieldIntegerList expected 3 arguments, got 4
1270-
# But there are 3 args apart from the self
1271-
feat.SetFieldIntegerList(bandname+'_cls', 3, unique.tolist())
1272-
feat.SetFieldIntegerList(bandname+'_cnt', 3, count.tolist())
1275+
1276+
# in this cas tho - the field_integerlist is a property (ie attribute)
1277+
# so not useful here but maybe later
1278+
# feat.field_integerlist = '(3:10,20,30)'
1279+
# feat.field_integer64list = [9876543210]
1280+
# feat.field_reallist = [123.5,567.0]
1281+
# feat.field_stringlist = ['abc','def']
1282+
1283+
# This VERY slow to write....
1284+
# It is also parsed as a string in gpd etc anyway so may
1285+
# it appears like this '(3:10,20,30)' (length: list)
1286+
# so not very practical for parsing later anyway
1287+
# as well just write as a string and use eval later
1288+
# field int idx input data
1289+
# feat.SetFieldIntegerList(fieldict[bandname+'_cls'], entry[1])
1290+
# feat.SetFieldIntegerList(fieldict[bandname+'_cnt'], entry[2])
1291+
1292+
# This is equally slow to write, but easier to parse
12731293
# A hack could be to write a string then use eval(string) to get a list back
1274-
#feat.SetField(bandname+'_cls', str(unique.tolist()))
1275-
#feat.SetField(bandname+'_cnt', str((count.tolist()))
1294+
ustr = str(unique.tolist())
1295+
cntstr = str(count.tolist())
1296+
feat.SetField(bandname+'_cls', ustr)
1297+
feat.SetField(bandname+'_cnt', cntstr)
12761298
vlyr.SetFeature(feat)
12771299
feat = vlyr.GetNextFeature()
12781300

@@ -1281,6 +1303,7 @@ def zonal_frac(inShp, inRas, band, bandname, layer=None,
12811303

12821304
vds = None
12831305
rds = None
1306+
12841307
frame = DataFrame(data=stats, columns=['fid', bandname+'_cls', bandname+'_cnt'])
12851308

12861309
if write_stat is None:

geospatial_learn/utilities.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -1455,7 +1455,7 @@ def apply_lut(src, lut):
14551455
dst = lut[src]
14561456
return dst
14571457

1458-
def colorscale(seg, prop='Area', custom=None):
1458+
def colorscale(seg, prop='Area', custom=None, zero=True):
14591459

14601460
"""
14611461
Colour an array according to a region prop value
@@ -1472,6 +1472,9 @@ def colorscale(seg, prop='Area', custom=None):
14721472
custom: list
14731473
a custom list of values to apply to array
14741474
1475+
zero: bool
1476+
whether to insert zero to represent nodata
1477+
14751478
Returns
14761479
-------
14771480
@@ -1487,7 +1490,8 @@ def colorscale(seg, prop='Area', custom=None):
14871490
alist = custom
14881491

14891492
# there must be a zero to represent no data so insert at start
1490-
alist.insert(0, 0)
1493+
if zero == True:
1494+
alist.insert(0, 0)
14911495
alist = np.array(alist)
14921496

14931497
oot = apply_lut(seg, alist)

0 commit comments

Comments
 (0)