6
6
# - C) conduct 1 pixle buffer of no-data class? (unsure if should be latter in workflow)
7
7
# 3. vectorise
8
8
# 4. simplify shapes to remove complixity
9
- # 5. join both data types back together as one Geopandas Geodataframe (container for sapely objects with projection imformation)
9
+ # 5. join both data types back together as one Geopandas Geodataframe (container for shapely objects with projection
10
+ # information)
10
11
# 6. export an a single shapefile with attributes intact.
11
12
12
- import fiona
13
- # Derived from https://github.com/GeoscienceAustralia/dea-notebooks/blob/KooieCate/vector_WOs_draft4.py
14
13
import geopandas
15
14
import geopandas as gp
16
15
import pandas as pd
17
16
import rasterio .features
18
17
import xarray as xr
19
18
from fiona .crs import from_epsg
20
19
from scipy import ndimage
21
- from shapely .geometry import shape , mapping
20
+ from shapely .geometry import shape
21
+ # Derived from https://github.com/GeoscienceAustralia/dea-notebooks/blob/KooieCate/vector_WOs_draft4.py
22
+ from typing import Tuple
22
23
23
24
24
- def load_data (url ):
25
- # Open geotiff and reformat to Xarray DataArray
26
- geotiff_wos = xr .open_rasterio (url ) # 'ga_s2am_wo_0-0-1_49JGN_2021-02-08_nrt_water.tif')
25
+ def load_wos_data (url ) -> xr . Dataset :
26
+ """ Open a GeoTIFF info an in memory DataArray """
27
+ geotiff_wos = xr .open_rasterio (url )
27
28
wos_dataset = geotiff_wos .to_dataset ('band' )
28
29
wos_dataset = wos_dataset .rename ({1 : 'wo' })
29
30
return wos_dataset
30
31
31
32
32
- def generate_raster_layers (wos_dataset ) :
33
+ def generate_raster_layers (wos_dataset : xr . Dataset ) -> Tuple [ xr . DataArray , xr . DataArray ] :
33
34
# Defining the three 'classes':
34
35
# a) Water: where water is observed. Bit value 128
35
- # b) unspoken 'dry'. this is not vectorised and is left and transparent layer. bit values: 1 (no data) 2 (Contiguity)
36
- # c) Not_analysed: every masking applied to the data except terrain shadow. bit values: composed of Everyting else,
36
+ # b) unspoken 'dry'. this is not vectorised and is left as a transparent layer. bit values: 1 (no data) 2 (
37
+ # Contiguity)
38
+ # c) Not_analysed: every masking applied to the data except terrain shadow. bit values: composed of everything else,
37
39
# 1 create binary arrays for two classes of interest
38
40
water_vals = (wos_dataset .wo == 128 ) # water only has 128 water observations
39
- # here we used reversed logic to turn all pixles that should be 'not analysed' to a value of 3. is is easier to list the 4 classes that are passed to the unlabled 'dry' class
41
+ # here we used reversed logic to turn all pixels that should be 'not analysed' to a value of 3. is is easier to
42
+ # list the 4 classes that are passed to the unlabled 'dry' class
40
43
not_analysed = wos_dataset .wo .where (((wos_dataset .wo == 0 ) | (wos_dataset .wo == 1 ) | (wos_dataset .wo == 8 )
41
44
| (wos_dataset .wo == 2 ) | (wos_dataset .wo == 128 ) | (wos_dataset .wo == 130 ) | (
42
45
wos_dataset .wo == 142 )), 3 )
43
- not_analysed = not_analysed .where ((not_analysed == 3 ), 0 ) # now keep the 3 values and make everyting else 0
44
- # 2 conduct binary errosion and closing to remove single pixles
46
+ not_analysed = not_analysed .where ((not_analysed == 3 ), 0 ) # now keep the 3 values and make everything else 0
47
+ # 2 conduct binary erosion and closing to remove single pixels
45
48
erroded_water = xr .DataArray (ndimage .binary_erosion (water_vals , iterations = 2 ).astype (water_vals .dtype ),
46
49
coords = water_vals .coords )
47
50
erroded_not_analysed = xr .DataArray (ndimage .binary_erosion (not_analysed , iterations = 2 ).astype (not_analysed .dtype ),
48
51
coords = not_analysed .coords )
49
- # dialating cloud 3 times after erroding 2, to create small overlap and iliminate gaps in data
52
+ # dilating cloud 3 times after eroding 2, to create small overlap and illuminate gaps in data
50
53
dilated_water = xr .DataArray (ndimage .binary_dilation (erroded_water , iterations = 3 ).astype (water_vals .dtype ),
51
54
coords = water_vals .coords )
52
55
dilated_not_analysed = xr .DataArray (
53
- ndimage .binary_dilation (erroded_not_analysed , iterations = ( 3 ) ).astype (not_analysed .dtype ),
56
+ ndimage .binary_dilation (erroded_not_analysed , iterations = 3 ).astype (not_analysed .dtype ),
54
57
coords = not_analysed .coords )
55
58
56
59
return dilated_water , dilated_not_analysed
57
60
58
61
59
- def vectorise_data (xarrayDataArray , transform , crs , label = 'Label' ):
62
+ def vectorise_data (data_array : xr . DataArray , transform , crs , label = 'Label' ):
60
63
"""this module takes an Xarray DataArray and vectorises it as shapely geometries in a Geopandas Geodataframe
61
64
62
65
Input
63
- xarrayDataArray: an Xarray DataArray with boolean values (1,0) with 1 or True equal to the areas that will be turned into vectors
64
- Label: default 'Label', String, the data label that will be added to each geometry in geodataframe
66
+ data_array: a DataArray with boolean values (1,0) with 1 or True equal to the areas that will be turned
67
+ into vectors
68
+ label: default 'Label', String, the data label that will be added to each geometry in geodataframe
65
69
66
70
output
67
- Geodataframe containing shapely geometies with data type lable in a series called attribute"""
71
+ Geodataframe containing shapely geometries with data type label in a series called attribute"""
68
72
69
73
vector = rasterio .features .shapes (
70
- xarrayDataArray .data .astype ('float32' ),
71
- mask = xarrayDataArray .data .astype ('float32' ) == 1 , # this defines which part of array becomes polygons
74
+ data_array .data .astype ('float32' ),
75
+ mask = data_array .data .astype ('float32' ) == 1 , # this defines which part of array becomes polygons
72
76
transform = transform )
73
77
74
- # rasterio.features.shapes outputs tupples . we only want the polygon coordinate portions of the tupples
75
- vectored_data = list (vector ) # put tupple output in list
78
+ # rasterio.features.shapes outputs tuples . we only want the polygon coordinate portions of the tuples
79
+ vectored_data = list (vector ) # put tuple output in list
76
80
77
81
# Extract the polygon coordinates from the list
78
82
polygons = [polygon for polygon , value in vectored_data ]
79
- # create empty list for lables
80
- labels = []
81
- # put in labels
82
- for i in polygons :
83
- labels .append (label ) # create a list with the data label type
83
+
84
+ # create a list with the data label type
85
+ labels = [label for _ in polygons ]
84
86
85
87
# Convert polygon coordinates into polygon shapes
86
88
polygons = [shape (polygon ) for polygon in polygons ]
@@ -93,7 +95,7 @@ def vectorise_data(xarrayDataArray, transform, crs, label='Label'):
93
95
94
96
95
97
def vectorise_wos_from_url (url ) -> geopandas .GeoDataFrame :
96
- raster = load_data (url )
98
+ raster = load_wos_data (url )
97
99
98
100
dataset_crs = from_epsg (raster .crs [11 :])
99
101
dataset_transform = raster .transform
@@ -102,26 +104,25 @@ def vectorise_wos_from_url(url) -> geopandas.GeoDataFrame:
102
104
dilated_water , dilated_not_analysed = generate_raster_layers (raster )
103
105
104
106
# vectorise the arrays
105
-
106
107
notAnalysedGPD = vectorise_data (dilated_not_analysed , dataset_transform , dataset_crs , label = 'Not_analysed' )
107
108
108
109
WaterGPD = vectorise_data (dilated_water , dataset_transform , dataset_crs , label = 'Water' )
109
110
110
111
# Simplify
111
112
112
- # Run simplification with 15 tollerance
113
- simplifyed_water = WaterGPD .simplify (10 )
113
+ # Run simplification with 15 tolerance
114
+ simplified_water = WaterGPD .simplify (10 )
114
115
115
- simplifyed_notAnalysed = notAnalysedGPD .simplify (15 )
116
+ simplified_not_analysed = notAnalysedGPD .simplify (15 )
116
117
117
118
# Put simplified shapes in a dataframe
118
- simple_waterGPD = gp .GeoDataFrame (geometry = simplifyed_water ,
119
+ simple_waterGPD = gp .GeoDataFrame (geometry = simplified_water ,
119
120
crs = dataset_crs )
120
121
121
- simple_notAnalysedGPD = gp .GeoDataFrame (geometry = simplifyed_notAnalysed ,
122
+ simple_notAnalysedGPD = gp .GeoDataFrame (geometry = simplified_not_analysed ,
122
123
crs = dataset_crs )
123
124
124
- # add attribute lables back in
125
+ # add attribute labels back in
125
126
simple_waterGPD ['attribute' ] = WaterGPD ['attribute' ]
126
127
127
128
simple_notAnalysedGPD ['attribute' ] = notAnalysedGPD ['attribute' ]
@@ -132,38 +133,7 @@ def vectorise_wos_from_url(url) -> geopandas.GeoDataFrame:
132
133
133
134
# 6 Join together and save to file
134
135
135
- All_classes = gp .GeoDataFrame (pd .concat ([simple_waterGPD , simple_notAnalysedGPD ], ignore_index = True ),
136
+ all_classes = gp .GeoDataFrame (pd .concat ([simple_waterGPD , simple_notAnalysedGPD ], ignore_index = True ),
136
137
crs = simple_notAnalysedGPD .crs )
137
138
138
- return All_classes
139
-
140
-
141
- def save_to_file (gpd_dataframe ):
142
- # +
143
- # #define output file name to save vectors as
144
- outFile = 'Test_simplify/WO_vectors_test_clean'
145
-
146
- # # Save the polygons to a shapefile
147
- schema = {
148
- 'geometry' : 'Polygon' ,
149
- 'properties' : {
150
- 'attribute' : 'str'
151
- }
152
- }
153
-
154
- # # Generate our dynamic filename
155
- FileName = f'{ outFile } .shp'
156
-
157
- # #create file and save
158
- with fiona .open (FileName ,
159
- "w" ,
160
- crs = from_epsg (3577 ),
161
- driver = 'ESRI Shapefile' ,
162
- schema = schema ) as output :
163
- for ix , poly in gpd_dataframe .iterrows ():
164
- output .write (({
165
- 'properties' : {
166
- 'attribute' : poly ['attribute' ]
167
- },
168
- 'geometry' : mapping (shape (poly ['geometry' ]))
169
- }))
139
+ return all_classes
0 commit comments