diff --git a/Segmentation.py b/Segmentation.py index e796a25a1cd9fff1aa809476ecb2278289fe4f3f..40ea191dfdd1c565055eef5607e11f57b45defd1 100644 --- a/Segmentation.py +++ b/Segmentation.py @@ -1,19 +1,19 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # This file is part of PHYMOBAT 1.2. # Copyright 2016 Sylvio Laventure (IRSTEA - UMR TETIS) -# +# # PHYMOBAT 1.2 is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. -# +# # PHYMOBAT 1.2 is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with PHYMOBAT 1.2. If not, see <http://www.gnu.org/licenses/>. @@ -31,9 +31,9 @@ from collections import * class Segmentation(Vector): """ - Vector class inherits the super vector class properties. This class create the final shapefile : Cartography + Vector class inherits the super vector class properties. This class create the final shapefile : Cartography on a input segmentation by decision tree. - + The output classname are (**out_class_name** variable): - Vegetation non naturelle - Vegetation semi-naturelle @@ -44,7 +44,7 @@ class Segmentation(Vector): - Forte phytomasse - Moyenne phytomasse - Faible phytomasse - + @param vector_used: Input/Output shapefile to clip (path) @type vector_used: str @@ -70,7 +70,7 @@ class Segmentation(Vector): Mainly 'Maj_count' (Majority Value) and 'Maj_count_perc' (Majority Percent) @type stats_rpg_tif: dict """ - + def __init__(self, used, cut): """ Create a new 'Clustering' instance @@ -79,10 +79,10 @@ class Segmentation(Vector): self.logger = Outils.Log("Log", "Segmentation") Vector.__init__(self, used, cut) - + self.stats_rpg_tif = {} self.output_file = 'Final_classification.shp' - + self.out_class_name = [] self.out_threshold = dict() @@ -91,7 +91,7 @@ class Segmentation(Vector): self.max_wood_idm = 0 self.max_wood_sfs = 0 self.max_bio = 0 - + def create_cartography(self, out_fieldnames, out_fieldtype): """ Function to create a output shapefile. In this output file, @@ -103,30 +103,31 @@ class Segmentation(Vector): :param out_fieldtype: List of outpu field type :type out_fieldtype: list of str """ - + + self.logger.debug("Create output shapefile : {0}".format(self.output_file)) shp_ogr_ds = self.data_source shp_ogr = self.data_source.GetLayer() - + # Projection # Import input shapefile projection srsObj = shp_ogr.GetSpatialRef() # Conversion to syntax ESRI srsObj.MorphToESRI() - + ## Remove the output final shapefile if it exists self.vector_used = self.output_file if os.path.exists(self.vector_used): self.data_source.GetDriver().DeleteDataSource(self.vector_used) - + out_ds = self.data_source.GetDriver().CreateDataSource(self.vector_used) - + if out_ds is None: self.logger.error('Could not create file') sys.exit(1) - + # Specific output layer out_layer = out_ds.CreateLayer(self.vector_used, srsObj, geom_type=ogr.wkbMultiPolygon) - + # Add new fields #Â To add RPG_CODE field out_fieldnames.insert(2,'RPG_CODE') @@ -143,45 +144,43 @@ class Segmentation(Vector): fieldDefn = ogr.FieldDefn('FBPHY_SUB', ogr.OFTInteger) out_layer.CreateField(fieldDefn) out_fieldnames.append('FBPHY_SUB') - + # Feature for the ouput shapefile featureDefn = out_layer.GetLayerDefn() - - in_feature = shp_ogr.SetNextByIndex(0) # Polygons initialisation - in_feature = shp_ogr.GetNextFeature() - + #Â Loop on input polygons - while in_feature: - + for idx in range(shp_ogr.GetFeatureCount()) : + + in_feature = shp_ogr.GetFeature(idx) geom = in_feature.GetGeometryRef() # Extract input geometry # Create a new polygon out_feature = ogr.Feature(featureDefn) - + # Set the polygon geometry and attribute out_feature.SetGeometry(geom) - # Set the existing ID + # Set the existing ID out_feature.SetField(out_fieldnames[0], in_feature.GetField(self.field_names[2])) # Set the area out_feature.SetField(out_fieldnames[1], geom.GetArea()/10000) - #Â Set the RPG column recouv_crops_RPG = 0 pourc_inter = self.stats_rpg_tif[in_feature.GetFID()]['Maj_count_perc'] if pourc_inter >= 85: recouv_crops_RPG = self.stats_rpg_tif[in_feature.GetFID()]['Maj_count'] - + out_feature.SetField('RPG_CODE', int(recouv_crops_RPG)) - + # Set the others polygons fields with the decision tree dictionnary for i in range(3, len(out_fieldnames)): + # If list stopped it on the second level, complete by empty case if len(self.class_tab_final[in_feature.GetFID()]) < len(out_fieldnames)-3 and \ self.class_tab_final[in_feature.GetFID()] != []: self.class_tab_final[in_feature.GetFID()].insert(len(self.class_tab_final[in_feature.GetFID()])-3,'') # To 3rd level self.class_tab_final[in_feature.GetFID()].insert(len(self.class_tab_final[in_feature.GetFID()])-3,0) # To degree - + try: # To the confusion matrix, replace level ++ by level -- if i == len(out_fieldnames)-1: @@ -191,48 +190,44 @@ class Segmentation(Vector): # if self.class_tab_final[in_feature.GetFID()][i-3] == 2: # Grassland to natural vegetation # self.class_tab_final[in_feature.GetFID()][i-3] = 1 + if self.class_tab_final[in_feature.GetFID()][i-3] > 7: # Phytomass to natural vegetation self.class_tab_final[in_feature.GetFID()][i-4] = 1 - + out_feature.SetField(str(out_fieldnames[i]), self.class_tab_final[in_feature.GetFID()][i-3]) except: - for i in range(3, len(out_fieldnames)-3): out_feature.SetField(str(out_fieldnames[i]), 'Undefined') - + out_feature.SetField('FBPHY_CODE', 255) out_feature.SetField('FBPHY_SUB', 255) # Append polygon to the output shapefile out_layer.CreateFeature(out_feature) - + # Destroy polygons - out_feature.Destroy() - in_feature.Destroy() - - # Next polygon - in_feature = shp_ogr.GetNextFeature() - + out_feature.Destroy() + # Close data - out_ds.Destroy() + out_ds.Destroy() def decision_tree(self, combin_tree): """ - Function to build the decision tree. Taking account output threshold and input + Function to build the decision tree. Taking account output threshold and input class name. - + @param combin_tree: Decision tree combination @type combin_tree: list of number class name """ - + # Combination tree on a sentence. Every sentence will be in a table. cond_tab = [] for ct in combin_tree: cond_a = '' #Â Condition Term c = 0 - while c < len(ct): - # Loop on tree combinaison + while c < len(ct): + # Loop on tree combinaison if self.out_threshold[ct[c]] == '' : #Â For interval condition cond_a = cond_a + 'self.stats_dict[ind_stats][' + str(ct[c]/2) + ']' +\ @@ -245,7 +240,7 @@ class Segmentation(Vector): c = c + 1 cond_tab.append(cond_a[:-5]) # Remove the last 'and' - # Loop on every value + # Loop on every value for ind_stats in range(len(self.stats_dict)): # Loop on decision tree combination. for cond in cond_tab: @@ -263,19 +258,19 @@ class Segmentation(Vector): for s in combin_tree[cond_tab.index(cond)]] + \ [combin_tree[cond_tab.index(cond)][len(combin_tree[cond_tab.index(cond)])-1]] + \ [combin_tree[cond_tab.index(cond)][len(combin_tree[cond_tab.index(cond)])-1]] - + def compute_biomass_density(self, method='SEATH'): """ Function to compute the biomass and density distribution. It returns threshold of biomass level. - + @param method: Classification method used. It can set 'SEATH' (by default) or 'RF' @type method: str """ - + if method == 'SEATH': distri = [v[1:] for k, v in self.stats_dict.items() if eval('v[0]' + self.out_threshold[1])] - + distri_bio = [] distri_den = [] for b in distri: @@ -283,11 +278,11 @@ class Segmentation(Vector): distri_bio.append(b) else: distri_den.append(b) - + elif method == 'RF': distri = [v[1:] for k, v in self.stats_dict.items() if not self.out_threshold["PREDICTION"][k] in [0,6,7]] - + distri_bio = [] distri_den = [] @@ -299,10 +294,10 @@ class Segmentation(Vector): else: distri_den.append(b) - #Â Transpose table + #Â Transpose table t_distri_bio = list(map(list, zip(*distri_bio))) t_distri_den = list(map(list, zip(*distri_den))) - + #Â Biomass threshold stdmore = (np.mean(t_distri_bio[2]) + np.std(t_distri_bio[2])) / np.max(t_distri_bio[2]) stdless = (np.mean(t_distri_bio[2]) - np.std(t_distri_bio[2])) / np.max(t_distri_bio[2]) @@ -314,21 +309,21 @@ class Segmentation(Vector): wood_sfs = np.array(t_distri_den[0], dtype=np.float64) wood_idm = np.array(t_distri_den[1], dtype=np.float64) max_bio = np.array(t_distri_den[2], dtype=np.float64) - + self.max_wood_sfs = np.nanmax(wood_sfs) self.max_wood_idm = np.nanmax(wood_idm) self.max_bio = np.nanmax(max_bio) - + def append_scale(self, select_class, form): """ Function to complete the 'class_tab_final' list with density and biomass information. This list will be used to build the final shapefile. - + @param select_class: Class name to add degree @type select_class: str @param form: Formula to add degree - @type form: str + @type form: str """ for ind_stats in range(len(self.stats_dict)): @@ -356,5 +351,3 @@ class Segmentation(Vector): pass except IndexError: pass - - \ No newline at end of file