Пример #1
0
    def calculate_locally(self):
        if not self.cqi_setup_tab.use_custom.isChecked():
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Due to the options you have chosen, this calculation must occur offline. You MUST select a custom precipitation or evapotranspiration layer dataset."))
            return
        if len(self.cqi_setup_tab.use_custom_ppt.layer_list) == 0:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("You must add a yearly mean precipitation layer to your map before you can run the calculation."))
            return

        if len(self.cqi_setup_tab.use_custom_pet.layer_list) == 0:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("You must add mean potential evapotranspiration layer to your map before you can run the calculation."))
            return

        # Select the initial and final bands from initial and final datasets 
        # (in case there is more than one lc band per dataset)
        custom_pet_vrt = self.cqi_setup_tab.use_custom_pet.get_vrt()
        custom_ppt_vrt = self.cqi_setup_tab.use_custom_ppt.get_vrt()

        if self.aoi.calc_frac_overlap(QgsGeometry.fromRect(self.cqi_setup_tab.use_custom_ppt.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Area of interest is not entirely within the precipitation layer."))
            return

        if self.aoi.calc_frac_overlap(QgsGeometry.fromRect(self.cqi_setup_tab.use_custom_pet.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Area of interest is not entirely within the potential evapotranspiration layer."))
            return

        out_f = self.get_save_raster()
        if not out_f:
            return

        self.close()

        crosses_180th, geojsons = self.aoi.bounding_box_gee_geojson()
        val = []
        n = 1

        if self.area_tab.area_fromfile.isChecked():
            for f in self.aoi.get_layer_wgs84().getFeatures():
                # Get an OGR geometry from the QGIS geometry
                geom = f.geometry()
                val.append(geom)
                n += 1

            # stringify json object 
            val_string = '{}'.format(json.loads(val[0].asJson()))

            # create ogr geometry
            val_geom = ogr.CreateGeometryFromJson(val_string)
            # simplify polygon to tolerance of 0.003
            val_geom_simplified = val_geom.Simplify(0.003)

            # fetch coordinates from json  
            coords= json.loads(val_geom_simplified.ExportToJson())['coordinates']
            geometries = {
                "coordinates":coords,
                "type":"Polygon"
            }
        elif self.area_tab.area_fromadmin.isChecked():
            geometries ={
                "coordinates":self.get_admin_poly_geojson()['geometry']['coordinates'][0],
                "type":"Polygon"
            }
        elif self.area_tab.area_frompoint.isChecked():
            point = QgsPointXY(float(self.area_tab.area_frompoint_point_x.text()), float(self.area_tab.area_frompoint_point_y.text()))
            crs_src = QgsCoordinateReferenceSystem(self.area_tab.canvas.mapSettings().destinationCrs().authid())
            point = QgsCoordinateTransform(crs_src, self.aoi.crs_dst, QgsProject.instance()).transform(point)
            geometries = json.loads(QgsGeometry.fromPointXY(point).asJson())

        # write aoi geometry to file for masking output
        aoi_geom = {
            "type": "FeatureCollection",
            "features": [
                {
                "type": "Feature",
                "properties": {},
                "geometry": geometries
                }
            ]
        }

        aoi_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'aoi.geojson')
        with open(aoi_file, 'w') as filetowrite:
            filetowrite.write(json.dumps(aoi_geom))

        # Add the custom layers to a VRT in case they don't match in resolution, 
        # and set proper output bounds
        in_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name
        gdal.BuildVRT(in_vrt,
                      [custom_pet_vrt, custom_ppt_vrt], 
                      resolution='highest', 
                      resampleAlg=gdal.GRA_NearestNeighbour,
                      outputBounds=self.aoi.get_aligned_output_bounds_deprecated(custom_ppt_vrt),
                      separate=True)

        lc_change_worker = StartWorker(ClimateQualityWorker,
                                       'calculating climate quality index', in_vrt, 
                                       out_f)
        if not lc_change_worker.success:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Error calculating climate quality index."), None)
            return

        band_info = [BandInfo("Climate Quality Index (year)", add_to_map=True)]
        out_json = os.path.splitext(out_f)[0] + '.json'
        create_local_json_metadata(out_json, out_f, band_info)
        schema = BandInfoSchema()
        for band_number in range(len(band_info)):
            b = schema.dump(band_info[band_number])
            if b['add_to_map']:
                # The +1 is because band numbers start at 1, not zero
                add_layer(out_f, band_number + 1, b)
Пример #2
0
    def calculate_locally(self):
        if not self.groupBox_custom_SOC.isChecked():
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Due to the options you have chosen, this calculation must occur offline. You MUST select a custom soil organic carbon dataset."
                   ))
            return
        if not self.lc_setup_tab.use_custom.isChecked():
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Due to the options you have chosen, this calculation must occur offline. You MUST select a custom land cover dataset."
                   ))
            return

        if len(self.comboBox_custom_soc.layer_list) == 0:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("You must add a soil organic carbon layer to your map before you can run the calculation."
                   ))
            return

        year_baseline = self.lc_setup_tab.get_initial_year()
        year_target = self.lc_setup_tab.get_final_year()
        if int(year_baseline) >= int(year_target):
            QtWidgets.QMessageBox.information(
                None, self.tr("Warning"),
                self.
                tr('The baseline year ({}) is greater than or equal to the target year ({}) - this analysis might generate strange results.'
                   .format(year_baseline, year_target)))

        if self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(self.lc_setup_tab.use_custom_initial.
                                     get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Area of interest is not entirely within the initial land cover layer."
                   ))
            return

        if self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(self.lc_setup_tab.use_custom_final.
                                     get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Area of interest is not entirely within the final land cover layer."
                   ))
            return

        out_f = self.get_save_raster()
        if not out_f:
            return

        self.close()

        # Select the initial and final bands from initial and final datasets
        # (in case there is more than one lc band per dataset)
        lc_initial_vrt = self.lc_setup_tab.use_custom_initial.get_vrt()
        lc_final_vrt = self.lc_setup_tab.use_custom_final.get_vrt()
        lc_files = [lc_initial_vrt, lc_final_vrt]
        lc_years = [
            self.lc_setup_tab.get_initial_year(),
            self.lc_setup_tab.get_final_year()
        ]
        lc_vrts = []
        for i in range(len(lc_files)):
            f = tempfile.NamedTemporaryFile(suffix='.vrt').name
            # Add once since band numbers don't start at zero
            gdal.BuildVRT(
                f,
                lc_files[i],
                bandList=[i + 1],
                outputBounds=self.aoi.get_aligned_output_bounds_deprecated(
                    lc_initial_vrt),
                resolution='highest',
                resampleAlg=gdal.GRA_NearestNeighbour,
                separate=True)
            lc_vrts.append(f)

        soc_vrt = self.comboBox_custom_soc.get_vrt()
        climate_zones = os.path.join(os.path.dirname(__file__), 'data',
                                     'IPCC_Climate_Zones.tif')
        in_files = [soc_vrt, climate_zones]
        in_files.extend(lc_vrts)
        in_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name
        log(u'Saving SOC input files to {}'.format(in_vrt))
        gdal.BuildVRT(
            in_vrt,
            in_files,
            resolution='highest',
            resampleAlg=gdal.GRA_NearestNeighbour,
            outputBounds=self.aoi.get_aligned_output_bounds_deprecated(
                lc_initial_vrt),
            separate=True)
        # Lc bands start on band 3 as band 1 is initial soc, and band 2 is
        # climate zones
        lc_band_nums = np.arange(len(lc_files)) + 3

        log(u'Saving soil organic carbon to {}'.format(out_f))
        soc_worker = StartWorker(SOCWorker,
                                 'calculating change in soil organic carbon',
                                 in_vrt, out_f, lc_band_nums, lc_years,
                                 self.get_fl())

        if not soc_worker.success:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.tr("Error calculating change in soil organic carbon."))
            return

        band_infos = [
            BandInfo("Soil organic carbon (degradation)",
                     add_to_map=True,
                     metadata={
                         'year_start': lc_years[0],
                         'year_end': lc_years[-1]
                     })
        ]
        for year in lc_years:
            if (year == lc_years[0]) or (year == lc_years[-1]):
                # Add first and last years to map
                add_to_map = True
            else:
                add_to_map = False
            band_infos.append(
                BandInfo("Soil organic carbon",
                         add_to_map=add_to_map,
                         metadata={'year': year}))
        for year in lc_years:
            band_infos.append(
                BandInfo("Land cover (7 class)", metadata={'year': year}))

        out_json = os.path.splitext(out_f)[0] + '.json'
        create_local_json_metadata(out_json, out_f, band_infos)
        schema = BandInfoSchema()
        for band_number in range(len(band_infos)):
            b = schema.dump(band_infos[band_number])
            if b['add_to_map']:
                # The +1 is because band numbers start at 1, not zero
                add_layer(out_f, band_number + 1, b)
Пример #3
0
    def calculate_locally(self):
        trans_matrix = [[
            11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27, 31, 32, 33,
            34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 51, 52, 53, 54, 55, 56,
            57, 61, 62, 63, 64, 65, 66, 67
        ],
                        self.lc_define_deg_tab.trans_matrix_get()]

        # Remap the persistence classes so they are sequential, making them
        # easier to assign a clear color ramp in QGIS
        persistence_remap = [[
            11, 12, 13, 14, 15, 16, 17, 21, 22, 23, 24, 25, 26, 27, 31, 32, 33,
            34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 47, 51, 52, 53, 54, 55, 56,
            57, 61, 62, 63, 64, 65, 66, 67, 71, 72, 73, 74, 75, 76, 77
        ],
                             [
                                 1, 12, 13, 14, 15, 16, 17, 21, 2, 23, 24, 25,
                                 26, 27, 31, 32, 3, 34, 35, 36, 37, 41, 42, 43,
                                 4, 45, 46, 47, 51, 52, 53, 54, 5, 56, 57, 61,
                                 62, 63, 64, 65, 6, 67, 71, 72, 73, 74, 75, 76,
                                 7
                             ]]

        if not self.lc_setup_tab.use_custom.isChecked():
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Due to the options you have chosen, this calculation must occur offline. You MUST select a custom land cover dataset."
                   ))
            return

        if len(self.lc_setup_tab.use_custom_initial.layer_list) == 0:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("You must add an initial land cover layer to your map before you can run the calculation."
                   ))
            return

        if len(self.lc_setup_tab.use_custom_final.layer_list) == 0:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("You must add a final land cover layer to your map before you can run the calculation."
                   ))
            return

        # Select the initial and final bands from initial and final datasets
        # (in case there is more than one lc band per dataset)
        lc_initial_vrt = self.lc_setup_tab.use_custom_initial.get_vrt()
        lc_final_vrt = self.lc_setup_tab.use_custom_final.get_vrt()

        year_baseline = self.lc_setup_tab.get_initial_year()
        year_target = self.lc_setup_tab.get_final_year()
        if int(year_baseline) >= int(year_target):
            QtWidgets.QMessageBox.information(
                None, self.tr("Warning"),
                self.
                tr('The initial year ({}) is greater than or equal to the target year ({}) - this analysis might generate strange results.'
                   .format(year_baseline, year_target)))

        if self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(self.lc_setup_tab.use_custom_initial.
                                     get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Area of interest is not entirely within the initial land cover layer."
                   ))
            return

        if self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(self.lc_setup_tab.use_custom_initial.
                                     get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Area of interest is not entirely within the final land cover layer."
                   ))
            return

        out_f = self.get_save_raster()
        if not out_f:
            return

        self.close()

        # Add the lc layers to a VRT in case they don't match in resolution,
        # and set proper output bounds
        in_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name
        gdal.BuildVRT(
            in_vrt, [lc_initial_vrt, lc_final_vrt],
            resolution='highest',
            resampleAlg=gdal.GRA_NearestNeighbour,
            outputBounds=self.aoi.get_aligned_output_bounds_deprecated(
                lc_initial_vrt),
            separate=True)

        lc_change_worker = StartWorker(LandCoverChangeWorker,
                                       'calculating land cover change', in_vrt,
                                       out_f, trans_matrix, persistence_remap)
        if not lc_change_worker.success:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.tr("Error calculating land cover change."), None)
            return

        band_info = [
            BandInfo("Land cover (degradation)",
                     add_to_map=True,
                     metadata={
                         'year_baseline': year_baseline,
                         'year_target': year_target
                     }),
            BandInfo("Land cover (7 class)", metadata={'year': year_baseline}),
            BandInfo("Land cover (7 class)", metadata={'year': year_target}),
            BandInfo("Land cover transitions",
                     add_to_map=True,
                     metadata={
                         'year_baseline': year_baseline,
                         'year_target': year_target
                     })
        ]
        out_json = os.path.splitext(out_f)[0] + '.json'
        create_local_json_metadata(out_json, out_f, band_info)
        schema = BandInfoSchema()
        for band_number in range(len(band_info)):
            b = schema.dump(band_info[band_number])
            if b['add_to_map']:
                # The +1 is because band numbers start at 1, not zero
                add_layer(out_f, band_number + 1, b)
Пример #4
0
    def btn_calculate(self):
        ######################################################################
        # Check that all needed output files are selected
        if not self.output_file_layer.text():
            QtWidgets.QMessageBox.information(
                None, self.tr("Error"),
                self.
                tr("Choose an output file for the biomass difference layers."))
            return

        if not self.output_file_table.text():
            QtWidgets.QMessageBox.information(
                None, self.tr("Error"),
                self.tr("Choose an output file for the summary table."))
            return

        # Note that the super class has several tests in it - if they fail it
        # returns False, which would mean this function should stop execution
        # as well.
        ret = super(DlgCalculateRestBiomassSummaryTable, self).btn_calculate()
        if not ret:
            return

        ######################################################################
        # Check that all needed input layers are selected
        if len(self.combo_layer_biomass_diff.layer_list) == 0:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("You must add a biomass layer to your map before you can use the summary tool."
                   ))
            return
        #######################################################################
        # Check that the layers cover the full extent needed
        if self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(
                    self.combo_layer_biomass_diff.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Area of interest is not entirely within the biomass layer."
                   ))
            return

        self.close()

        #######################################################################
        # Prep files
        in_file = self.combo_layer_biomass_diff.get_data_file()

        # Remember the first value is an indication of whether dataset is
        # wrapped across 180th meridian
        wkts = self.aoi.meridian_split('layer', 'wkt', warn=False)[1]
        bbs = self.aoi.get_aligned_output_bounds(in_file)

        output_biomass_diff_tifs = []
        output_biomass_diff_json = self.output_file_layer.text()
        for n in range(len(wkts)):
            if len(wkts) > 1:
                output_biomass_diff_tif = os.path.splitext(
                    output_biomass_diff_json)[0] + '_{}.tif'.format(n)
            else:
                output_biomass_diff_tif = os.path.splitext(
                    output_biomass_diff_json)[0] + '.tif'
            output_biomass_diff_tifs.append(output_biomass_diff_tif)

            log(u'Saving clipped biomass file to {}'.format(
                output_biomass_diff_tif))
            geojson = json_geom_to_geojson(
                QgsGeometry.fromWkt(wkts[n]).asJson())
            clip_worker = StartWorker(
                ClipWorker,
                'masking layers (part {} of {})'.format(n + 1, len(wkts)),
                in_file, output_biomass_diff_tif, geojson, bbs[n])
            if not clip_worker.success:
                QtWidgets.QMessageBox.critical(
                    None, self.tr("Error"),
                    self.tr("Error masking input layers."))
                return

            ######################################################################
            #  Calculate biomass change summary table
            log('Calculating summary table...')
            rest_summary_worker = StartWorker(
                RestBiomassSummaryWorker,
                'calculating summary table (part {} of {})'.format(
                    n + 1, len(wkts)), output_biomass_diff_tif)
            if not rest_summary_worker.success:
                QtWidgets.QMessageBox.critical(
                    None, self.tr("Error"),
                    self.tr("Error calculating biomass change summary table."))
                return
            else:
                if n == 0:
                    biomass_initial, \
                        biomass_change, \
                        area_site = rest_summary_worker.get_return()
                else:
                    this_biomass_initial, \
                        this_biomass_change, \
                        this_area_site = rest_summary_worker.get_return()
                    biomass_initial = biomass_initial + this_biomass_initial
                    biomass_change = biomass_change + this_biomass_change
                    area_site = area_site + this_area_site

        log('area_site: {}'.format(area_site))
        log('biomass_initial: {}'.format(biomass_initial))
        log('biomass_change: {}'.format(biomass_change))

        # Figure out how many years of restoration this data is for, take this
        # from the second band in the in file
        band_infos = get_band_infos(in_file)
        length_yr = band_infos[1]['metadata']['years']
        # And make a list of the restoration types
        rest_types = [
            band_info['metadata']['type']
            for band_info in band_infos[1:len(band_infos)]
        ]

        make_summary_table(self.output_file_table.text(), biomass_initial,
                           biomass_change, area_site, length_yr, rest_types)

        # Add the biomass_dif layers to the map
        if len(output_biomass_diff_tifs) == 1:
            output_file = output_biomass_diff_tifs[0]
        else:
            output_file = os.path.splitext(
                output_biomass_diff_json)[0] + '.vrt'
            gdal.BuildVRT(output_file, output_biomass_diff_tifs)
        # Update the band infos to use the masking value (-32767) as the file
        # no data value, so that stretches are more likely to compute correctly
        for item in band_infos:
            item['no_data_value'] = -32767
        create_local_json_metadata(
            output_biomass_diff_json,
            output_file,
            band_infos,
            metadata={
                'task_name': self.options_tab.task_name.text(),
                'task_notes': self.options_tab.task_notes.toPlainText()
            })
        schema = BandInfoSchema()
        for n in range(1, len(band_infos)):
            add_layer(output_file, n + 1, schema.dump(band_infos[n]))

        return True
Пример #5
0
    def btn_calculate(self):
        # Note that the super class has several tests in it - if they fail it
        # returns False, which would mean this function should stop execution
        # as well.
        ret = super(DlgCalculateSDISummaryTableAdmin, self).btn_calculate()
        if not ret:
            return

        ######################################################################
        # Check that all needed input layers are selected

        if len(self.combo_layer_sqi.layer_list) == 0:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("You must add a Soil Quality Indicator layer to your map before you can use the SDI calculation tool."
                   ))
            return

        if len(self.combo_layer_vqi.layer_list) == 0:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("You must add a Vegatation Quality Indicator layer to your map before you can use the SDI calculation tool."
                   ))
            return

        if len(self.combo_layer_cqi.layer_list) == 0:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("You must add a Climate Quality Indicator layer to your map before you can use the SDI calculation tool."
                   ))
            return

        if len(self.combo_layer_mqi.layer_list) == 0:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("You must add a Management Quality Indicator layer to your map before you can use the SDI calculation tool."
                   ))
            return

        #######################################################################
        # Check that the layers cover the full extent needed
        if self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(
                    self.combo_layer_sqi.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Area of interest is not entirely within the Soil Quality Indicator layer."
                   ))
            return

        if self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(
                    self.combo_layer_vqi.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Area of interest is not entirely within the Vegetation Quality Indicator layer."
                   ))
            return

        if self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(
                    self.combo_layer_cqi.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Area of interest is not entirely within the Climate Quality Indicator layer."
                   ))
            return

        if self.aoi.calc_frac_overlap(
                QgsGeometry.fromRect(
                    self.combo_layer_mqi.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.
                tr("Area of interest is not entirely within the Management Quality Indicator layer."
                   ))
            return

        out_f = self.get_save_raster()
        if not out_f:
            return

        self.close()

        crosses_180th, geojsons = self.aoi.bounding_box_gee_geojson()
        val = []
        n = 1

        if self.area_tab.area_fromfile.isChecked():
            for f in self.aoi.get_layer_wgs84().getFeatures():
                # Get an OGR geometry from the QGIS geometry
                geom = f.geometry()
                val.append(geom)
                n += 1

            # stringify json object
            val_string = '{}'.format(json.loads(val[0].asJson()))

            # create ogr geometry
            val_geom = ogr.CreateGeometryFromJson(val_string)
            # simplify polygon to tolerance of 0.003
            val_geom_simplified = val_geom.Simplify(0.003)

            # fetch coordinates from json
            coords = json.loads(
                val_geom_simplified.ExportToJson())['coordinates']
            geometries = {"coordinates": coords, "type": "Polygon"}
        elif self.area_tab.area_fromadmin.isChecked():
            geometries = {
                "coordinates":
                self.get_admin_poly_geojson()['geometry']['coordinates'][0],
                "type":
                "Polygon"
            }
        elif self.area_tab.area_frompoint.isChecked():
            point = QgsPointXY(
                float(self.area_tab.area_frompoint_point_x.text()),
                float(self.area_tab.area_frompoint_point_y.text()))
            crs_src = QgsCoordinateReferenceSystem(
                self.area_tab.canvas.mapSettings().destinationCrs().authid())
            point = QgsCoordinateTransform(
                crs_src, self.aoi.crs_dst,
                QgsProject.instance()).transform(point)
            geometries = json.loads(QgsGeometry.fromPointXY(point).asJson())

        # write aoi geometry to file for masking output
        aoi_geom = {
            "type":
            "FeatureCollection",
            "features": [{
                "type": "Feature",
                "properties": {},
                "geometry": geometries
            }]
        }

        aoi_file = os.path.join(os.path.dirname(os.path.realpath(__file__)),
                                'data', 'aoi.geojson')
        with open(aoi_file, 'w') as filetowrite:
            filetowrite.write(json.dumps(aoi_geom))

        #######################################################################
        # Load all datasets to VRTs (to select only the needed bands)

        sqi_vrt = self.combo_layer_sqi.get_vrt()
        vqi_vrt = self.combo_layer_vqi.get_vrt()
        cqi_vrt = self.combo_layer_cqi.get_vrt()
        mqi_vrt = self.combo_layer_mqi.get_vrt()

        # Add the custom layers to a VRT in case they don't match in resolution,
        # and set proper output bounds
        in_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name
        gdal.BuildVRT(
            in_vrt, [sqi_vrt, vqi_vrt, cqi_vrt, mqi_vrt],
            resolution='highest',
            resampleAlg=gdal.GRA_NearestNeighbour,
            outputBounds=self.aoi.get_aligned_output_bounds_deprecated(
                sqi_vrt),
            separate=True)

        lc_change_worker = StartWorker(
            SDIWorker, 'calculating Sensitivity Desertification index', in_vrt,
            out_f)
        if not lc_change_worker.success:
            QtWidgets.QMessageBox.critical(
                None, self.tr("Error"),
                self.tr(
                    "Error calculating Sensitivity Desertification index."),
                None)
            return

        band_info = [
            BandInfo("Sensitivity Desertification Index", add_to_map=True)
        ]
        out_json = os.path.splitext(out_f)[0] + '.json'
        create_local_json_metadata(out_json, out_f, band_info)
        schema = BandInfoSchema()
        for band_number in range(len(band_info)):
            b = schema.dump(band_info[band_number])
            if b['add_to_map']:
                # The +1 is because band numbers start at 1, not zero
                add_layer(out_f, band_number + 1, b)
Пример #6
0
    def calculate_locally(self):
        if not self.sqi_setup_tab.use_custom.isChecked():
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Due to the options you have chosen, this calculation must occur offline. You MUST select a custom land cover dataset."))
            return
            
        if len(self.sqi_setup_tab.use_custom_pm.layer_list) == 0:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("You must add an parent material layer to your map before you can run the calculation."))
            return

        if len(self.sqi_setup_tab.use_custom_rock_frag.layer_list) == 0:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("You must add a rock fragment layer to your map before you can run the calculation."))
            return
        
        if len(self.sqi_setup_tab.use_custom_texture.layer_list) == 0:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("You must add a soil texture layer to your map before you can run the calculation."))
            return

        if len(self.sqi_setup_tab.use_custom_drainage.layer_list) == 0:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("You must add a drainage layer to your map before you can run the calculation."))
            return

        # select the parent material, slope, soil texture, drainage and rock fragment datasets 
        pm_vrt = self.sqi_setup_tab.use_custom_pm.get_vrt()
        drainage_vrt = self.sqi_setup_tab.use_custom_drainage.get_vrt()
        texture_vrt = self.sqi_setup_tab.use_custom_texture.get_vrt()
        rock_frag_vrt = self.sqi_setup_tab.use_custom_rock_frag.get_vrt()

        soil_depth = self.sqi_setup_tab.use_depth.value()

        if self.aoi.calc_frac_overlap(QgsGeometry.fromRect(self.sqi_setup_tab.use_custom_pm.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Area of interest is not entirely within the parent material layer."))
            return

        if self.aoi.calc_frac_overlap(QgsGeometry.fromRect(self.sqi_setup_tab.use_custom_drainage.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Area of interest is not entirely within the drainage layer."))
            return
        if self.aoi.calc_frac_overlap(QgsGeometry.fromRect(self.sqi_setup_tab.use_custom_rock_frag.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Area of interest is not entirely within the rock fragment layer."))
            return

        if self.aoi.calc_frac_overlap(QgsGeometry.fromRect(self.sqi_setup_tab.use_custom_texture.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Area of interest is not entirely within the soil texture layer."))
            return

        out_f = self.get_save_raster()
        if not out_f:
            return

        self.close()

        crosses_180th, geojsons = self.aoi.bounding_box_gee_geojson()
        val = []
        n = 1

        if self.area_tab.area_fromfile.isChecked():
            for f in self.aoi.get_layer_wgs84().getFeatures():
                # Get an OGR geometry from the QGIS geometry
                geom = f.geometry()
                val.append(geom)
                n += 1

            # stringify json object 
            val_string = '{}'.format(json.loads(val[0].asJson()))

            # create ogr geometry
            val_geom = ogr.CreateGeometryFromJson(val_string)
            # simplify polygon to tolerance of 0.003
            val_geom_simplified = val_geom.Simplify(0.003)

            # fetch coordinates from json  
            coords= json.loads(val_geom_simplified.ExportToJson())['coordinates']
            geometries = {
                "coordinates":coords,
                "type":"Polygon"
            }
        elif self.area_tab.area_fromadmin.isChecked():
            geometries ={
                "coordinates":self.get_admin_poly_geojson()['geometry']['coordinates'][0],
                "type":"Polygon"
            }
        elif self.area_tab.area_frompoint.isChecked():
            point = QgsPointXY(float(self.area_tab.area_frompoint_point_x.text()), float(self.area_tab.area_frompoint_point_y.text()))
            crs_src = QgsCoordinateReferenceSystem(self.area_tab.canvas.mapSettings().destinationCrs().authid())
            point = QgsCoordinateTransform(crs_src, self.aoi.crs_dst, QgsProject.instance()).transform(point)
            geometries = json.loads(QgsGeometry.fromPointXY(point).asJson())

        # write aoi geometry to file for masking output
        aoi_geom = {
            "type": "FeatureCollection",
            "features": [
                {
                "type": "Feature",
                "properties": {},
                "geometry": geometries
                }
            ]
        }

        aoi_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'aoi.geojson')
        with open(aoi_file, 'w') as filetowrite:
            filetowrite.write(json.dumps(aoi_geom))

        # Add the sqi layers to a VRT in case they don't match in resolution, 
        # and set proper output bounds
        sqi_files = [pm_vrt, texture_vrt, drainage_vrt, rock_frag_vrt]
        # in_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name
        sqi_vrts = []

        for i in range(len(sqi_files)):
            f = tempfile.NamedTemporaryFile(suffix='.vrt').name
            gdal.BuildVRT(f,
                        sqi_files[i], 
                        bandList=[i + 1],
                        resolution='highest', 
                        resampleAlg=gdal.GRA_NearestNeighbour,
                        outputBounds=self.aoi.get_aligned_output_bounds_deprecated(texture_vrt),
                        separate=True)

            sqi_vrts.append(f)

        slope_v = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'slope.tif')

        if not os.path.exists(slope_v):
            slope_zip = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data', 'slope.zip')
            slope_unzip = ZipFile(slope_zip)
            slope_unzip.extractall(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data'))

            slope_unzip.close()
            log("Extracting slope file...")

        else:
            log("Slope.tif already exists")

        in_files = [slope_v]
        in_files.extend(sqi_vrts)
        in_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name
        log(u'Saving SQI input files to {}'.format(in_vrt))

        gdal.BuildVRT(in_vrt,
                        in_files, 
                        resolution='highest', 
                        resampleAlg=gdal.GRA_NearestNeighbour,
                        outputBounds=self.aoi.get_aligned_output_bounds_deprecated(texture_vrt),
                        separate=True)

        # sqi_band_nums = np.arange(len(lc_files)) + 6

        sqi_worker = StartWorker(SoilQualityWorker,
                                       'calculating soil quality index', soil_depth, in_vrt, 
                                       out_f )

        if not sqi_worker.success:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Error calculating soil quality index."), None)
            return

        band_info = [BandInfo("Soil Quality Index (cm deep)", add_to_map=True, metadata={'depth': soil_depth})]
        out_json = os.path.splitext(out_f)[0] + '.json'
        create_local_json_metadata(out_json, out_f, band_info)
        schema = BandInfoSchema()
        for band_number in range(len(band_info)):
            b = schema.dump(band_info[band_number])
            if b['add_to_map']:
                # The +1 is because band numbers start at 1, not zero
                add_layer(out_f, band_number + 1, b)
Пример #7
0
    def btn_calculate(self):
        ######################################################################
        # Check that all needed output files are selected
        if not self.output_file_layer.text():
            QtWidgets.QMessageBox.information(None, self.tr("Error"),
                                          self.tr("Choose an output file for the indicator layer."))
            return

        if not self.output_file_table.text():
            QtWidgets.QMessageBox.information(None, self.tr("Error"),
                                          self.tr("Choose an output file for the summary table."))
            return

        # Note that the super class has several tests in it - if they fail it
        # returns False, which would mean this function should stop execution
        # as well.
        ret = super(DlgCalculateUrbanSummaryTable, self).btn_calculate()
        if not ret:
            return

        ######################################################################
        # Check that all needed input layers are selected
        if len(self.combo_layer_urban_series.layer_list) == 0:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("You must add an urban series layer to your map before you can use the urban change summary tool."))
            return

        #######################################################################
        # Check that the layers cover the full extent needed
        if self.aoi.calc_frac_overlap(QgsGeometry.fromRect(self.combo_layer_urban_series.get_layer().extent())) < .99:
            QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                       self.tr("Area of interest is not entirely within the urban series layer."))
            return

        self.close()

        #######################################################################
        # Load all datasets to VRTs (to select only the needed bands)
        band_infos = get_band_infos(self.combo_layer_urban_series.get_data_file())

        urban_annual_band_indices = [i for i, bi in enumerate(band_infos) if bi['name'] == 'Urban']
        urban_annual_band_indices.sort(key=lambda i: band_infos[i]['metadata']['year'])
        urban_years = [bi['metadata']['year'] for bi in band_infos if bi['name'] == 'Urban']
        urban_files = []
        for i in urban_annual_band_indices:
            f = tempfile.NamedTemporaryFile(suffix='.vrt').name
            # Add once since band numbers don't start at zero
            gdal.BuildVRT(f,
                          self.combo_layer_urban_series.get_data_file(),
                          bandList=[i + 1])
            urban_files.append(f)


        pop_annual_band_indices = [i for i, bi in enumerate(band_infos) if bi['name'] == 'Population']
        pop_annual_band_indices.sort(key=lambda i: band_infos[i]['metadata']['year'])
        pop_years = [bi['metadata']['year'] for bi in band_infos if bi['name'] == 'Population']
        pop_files = []
        for i in pop_annual_band_indices:
            f = tempfile.NamedTemporaryFile(suffix='.vrt').name
            # Add once since band numbers don't start at zero
            gdal.BuildVRT(f,
                          self.combo_layer_urban_series.get_data_file(),
                          bandList=[i + 1])
            pop_files.append(f)

        assert (len(pop_files) == len(urban_files))
        assert (urban_years == pop_years)

        in_files = list(urban_files)
        in_files.extend(pop_files)
        urban_band_nums = np.arange(len(urban_files)) + 1
        pop_band_nums = np.arange(len(pop_files)) + 1 + urban_band_nums.max()

        # Remember the first value is an indication of whether dataset is 
        # wrapped across 180th meridian
        wkts = self.aoi.meridian_split('layer', 'wkt', warn=False)[1]
        bbs = self.aoi.get_aligned_output_bounds(urban_files[1])

        ######################################################################
        # Process the wkts using a summary worker
        output_indicator_tifs = []
        output_indicator_json = self.output_file_layer.text()
        for n in range(len(wkts)):
            # Compute the pixel-aligned bounding box (slightly larger than 
            # aoi). Use this instead of croptocutline in gdal.Warp in order to 
            # keep the pixels aligned with the chosen productivity layer.
        
            # Combines SDG 15.3.1 input raster into a VRT and crop to the AOI
            indic_vrt = tempfile.NamedTemporaryFile(suffix='.vrt').name
            log(u'Saving indicator VRT to: {}'.format(indic_vrt))
            # The plus one is because band numbers start at 1, not zero
            gdal.BuildVRT(indic_vrt,
                          in_files,
                          outputBounds=bbs[n],
                          resolution='highest',
                          resampleAlg=gdal.GRA_NearestNeighbour,
                          separate=True)

            if len(wkts) > 1:
                output_indicator_tif = os.path.splitext(output_indicator_json)[0] + '_{}.tif'.format(n)
            else:
                output_indicator_tif = os.path.splitext(output_indicator_json)[0] + '.tif'
            output_indicator_tifs.append(output_indicator_tif)

            log(u'Saving urban clipped files to {}'.format(output_indicator_tif))
            geojson = json_geom_to_geojson(QgsGeometry.fromWkt(wkts[n]).asJson())
            clip_worker = StartWorker(ClipWorker, 'masking layers (part {} of {})'.format(n + 1, len(wkts)), 
                                      indic_vrt, output_indicator_tif,
                                      geojson, bbs[n])
            if not clip_worker.success:
                QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                           self.tr("Error masking urban change input layers."))
                return

            ######################################################################
            #  Calculate urban change table
            log('Calculating summary table...')
            urban_summary_worker = StartWorker(UrbanSummaryWorker,
                                               'calculating summary table (part {} of {})'.format(n + 1, len(wkts)),
                                               output_indicator_tif,
                                               urban_band_nums, pop_band_nums, 9)
            if not urban_summary_worker.success:
                QtWidgets.QMessageBox.critical(None, self.tr("Error"),
                                           self.tr("Error calculating urban change summary table."))
                return
            else:
                if n == 0:
                     areas, \
                             populations = urban_summary_worker.get_return()
                else:
                     these_areas, \
                             these_populations = urban_summary_worker.get_return()
                     areas = areas + these_areas
                     populations = populations + these_populations

        make_summary_table(areas, populations, self.output_file_table.text())

        # Add the indicator layers to the map
        output_indicator_bandinfos = [BandInfo("Urban", add_to_map=True, metadata={'year': 2000}),
                                      BandInfo("Urban", add_to_map=True, metadata={'year': 2005}),
                                      BandInfo("Urban", add_to_map=True, metadata={'year': 2010}),
                                      BandInfo("Urban", add_to_map=True, metadata={'year': 2015}),
                                      BandInfo("Population", metadata={'year': 2000}),
                                      BandInfo("Population", metadata={'year': 2005}),
                                      BandInfo("Population", metadata={'year': 2010}),
                                      BandInfo("Population", metadata={'year': 2015})]
        if len(output_indicator_tifs) == 1:
            output_file = output_indicator_tifs[0]
        else:
            output_file = os.path.splitext(output_indicator_json)[0] + '.vrt'
            gdal.BuildVRT(output_file, output_indicator_tifs)
        create_local_json_metadata(output_indicator_json, output_file, 
                output_indicator_bandinfos, metadata={'task_name': self.options_tab.task_name.text(),
                                                'task_notes': self.options_tab.task_notes.toPlainText()})
        schema = BandInfoSchema()
        add_layer(output_file, 1, schema.dump(output_indicator_bandinfos[0]))
        add_layer(output_file, 2, schema.dump(output_indicator_bandinfos[1]))
        add_layer(output_file, 3, schema.dump(output_indicator_bandinfos[2]))
        add_layer(output_file, 4, schema.dump(output_indicator_bandinfos[3]))

        return True