Exemple #1
0
    def save_hazard_layer(self, hazard_path):
        # download or copy hazard path/url
        # It is a single tif file
        if not hazard_path and not os.path.exists(self.hazard_path):
            raise IOError("Hazard file not specified")

        if hazard_path:
            temp_hazard = download_file(hazard_path)
            shutil.copy(temp_hazard, self.hazard_path)

        # copy qml and metadata
        shutil.copy(self.ash_fixtures_dir("hazard.qml"), self.working_dir_path("hazard.qml"))

        keyword_io = KeywordIO()

        keywords = {
            "hazard_category": u"single_event",
            "keyword_version": u"3.5",
            "title": u"Ash Fall",
            "hazard": u"volcanic_ash",
            "continuous_hazard_unit": u"centimetres",
            "layer_geometry": u"raster",
            "layer_purpose": u"hazard",
            "layer_mode": u"continuous",
        }

        hazard_layer = read_qgis_layer(self.hazard_path, "Ash Fall")
        keyword_io.write_keywords(hazard_layer, keywords)
Exemple #2
0
    def generate_population_aggregation(self):

        # duplicate exposure data
        QgsVectorFileWriter.writeAsVectorFormat(
            self.exposure_layer.as_qgis_native(),
            self.population_aggregate_path, 'CP1250', None, 'ESRI Shapefile')
        population_aggregate = read_qgis_layer(self.population_aggregate_path,
                                               'Impacted Population')
        shutil.copy(self.population_path.replace('.shp', '.xml'),
                    self.population_aggregate_path.replace('.shp', '.xml'))

        # add affected population field
        population_aggregate.startEditing()
        field = QgsField(self.affect_field, QVariant.Int)
        field2 = QgsField(self.target_field, QVariant.Int)
        population_aggregate.dataProvider().addAttributes([field, field2])
        population_aggregate.commitChanges()

        idx = population_aggregate.fieldNameIndex(self.affect_field)
        idx2 = population_aggregate.fieldNameIndex(self.target_field)
        impact_layer = self.impact_layer.as_qgis_native()
        keyword_io = KeywordIO()
        name_field = keyword_io.read_keywords(population_aggregate,
                                              'area_name_field')
        attribute_field = keyword_io.read_keywords(population_aggregate,
                                                   'field')

        # calculate affected data
        district_dict = {}
        for f in impact_layer.getFeatures():
            if f[self.target_field] >= 1:
                if f[name_field] in district_dict:
                    district_dict[f[name_field]] += f[attribute_field]
                else:
                    district_dict[f[name_field]] = f[attribute_field]

        population_aggregate.startEditing()
        for f in population_aggregate.getFeatures():
            f[idx] = district_dict.get(f[name_field], 0)
            if f[idx] == 0:
                # mark as unaffected
                f[idx2] = 0
            else:
                # mark as affected
                f[idx2] = 1

            population_aggregate.updateFeature(f)
        population_aggregate.commitChanges()
        self.affected_aggregate = district_dict

        # calculate total affected people
        total_affected = 0
        for k, v in district_dict.iteritems():
            total_affected += v

        # calculate new minimum needs
        min_needs = self.impact_data.minimum_needs
        for k, v in min_needs.iteritems():
            for need in v:
                need['amount'] = need['value'] * total_affected
Exemple #3
0
    def save_hazard_layer(self, hazard_path):
        # download or copy hazard path/url
        # It is a single tif file
        if not hazard_path and not os.path.exists(self.hazard_path):
            raise IOError('Hazard file not specified')

        if hazard_path:
            temp_hazard = download_file(hazard_path)
            shutil.copy(temp_hazard, self.hazard_path)

        # copy qml and metadata
        shutil.copy(self.ash_fixtures_dir('hazard.qml'),
                    self.working_dir_path('hazard.qml'))

        keyword_io = KeywordIO()

        keywords = {
            'hazard_category': u'single_event',
            'keyword_version': u'3.5',
            'title': u'Ash Fall',
            'hazard': u'volcanic_ash',
            'continuous_hazard_unit': u'centimetres',
            'layer_geometry': u'raster',
            'layer_purpose': u'hazard',
            'layer_mode': u'continuous'
        }

        hazard_layer = read_qgis_layer(self.hazard_path, 'Ash Fall')
        keyword_io.write_keywords(hazard_layer, keywords)
Exemple #4
0
    def generate_report(self):
        # Generate pdf report from impact
        if not self.impact_exists:
            # Cannot generate report when no impact layer present
            return

        layer_registry = QgsMapLayerRegistry.instance()
        layer_registry.removeAllMapLayers()
        impact_qgis_layer = read_qgis_layer(self.impact_layer.filename)
        layer_registry.addMapLayer(impact_qgis_layer)
        CANVAS.setExtent(impact_qgis_layer.extent())
        CANVAS.refresh()
        report = ImpactReport(IFACE, template=None, layer=impact_qgis_layer)

        report.print_map_to_pdf(self.map_report_path)
        report.print_impact_table(self.table_report_path)
        layer_registry.removeAllMapLayers()
Exemple #5
0
    def generate_report(self):
        # Generate pdf report from impact/hazard
        LOGGER.info("Generating report")
        if not self.impact_exists:
            # Cannot generate report when no impact layer present
            LOGGER.info("Cannot Generate report when no impact present.")
            return

        project_instance = QgsProject.instance()
        project_instance.setFileName(self.project_path)
        project_instance.read()

        # get layer registry
        layer_registry = QgsMapLayerRegistry.instance()
        layer_registry.removeAllMapLayers()

        # Set up the map renderer that will be assigned to the composition
        map_renderer = CANVAS.mapRenderer()

        # Enable on the fly CRS transformations
        map_renderer.setProjectionsEnabled(True)

        default_crs = map_renderer.destinationCrs()
        crs = QgsCoordinateReferenceSystem("EPSG:4326")
        map_renderer.setDestinationCrs(crs)

        # add place name layer
        layer_registry.addMapLayer(self.cities_layer, False)

        # add airport layer
        layer_registry.addMapLayer(self.airport_layer, False)

        # add volcano layer
        layer_registry.addMapLayer(self.volcano_layer, False)

        # add impact layer
        hazard_layer = read_qgis_layer(self.hazard_path, self.tr("People Affected"))
        layer_registry.addMapLayer(hazard_layer, False)

        # add basemap layer
        layer_registry.addMapLayer(self.highlight_base_layer, False)

        # add basemap layer
        layer_registry.addMapLayer(self.overview_layer, False)

        CANVAS.setExtent(hazard_layer.extent())
        CANVAS.refresh()

        template_path = self.ash_fixtures_dir("realtime-ash.qpt")

        with open(template_path) as f:
            template_content = f.read()

        document = QDomDocument()
        document.setContent(template_content)

        # Now set up the composition
        # map_settings = QgsMapSettings()
        # composition = QgsComposition(map_settings)
        composition = QgsComposition(map_renderer)

        subtitution_map = self.event_dict()
        LOGGER.debug(subtitution_map)

        # load composition object from template
        result = composition.loadFromTemplate(document, subtitution_map)
        if not result:
            LOGGER.exception("Error loading template %s with keywords\n %s", template_path, subtitution_map)
            raise MapComposerError

        # get main map canvas on the composition and set extent
        map_impact = composition.getComposerItemById("map-impact")
        if map_impact:
            map_impact.zoomToExtent(hazard_layer.extent())
            map_impact.renderModeUpdateCachedImage()
        else:
            LOGGER.exception("Map canvas could not be found in template %s", template_path)
            raise MapComposerError

        # get overview map canvas on the composition and set extent
        map_overall = composition.getComposerItemById("map-overall")
        if map_overall:
            map_overall.setLayerSet([self.overview_layer.id()])
            # this is indonesia extent
            indonesia_extent = QgsRectangle(
                94.0927980005593554, -15.6629591962689343, 142.0261493318861312, 10.7379406374101816
            )
            map_overall.zoomToExtent(indonesia_extent)
            map_overall.renderModeUpdateCachedImage()
        else:
            LOGGER.exception("Map canvas could not be found in template %s", template_path)
            raise MapComposerError

        # setup impact table
        self.render_population_table()
        self.render_nearby_table()
        self.render_landcover_table()

        impact_table = composition.getComposerItemById("table-impact")
        if impact_table is None:
            message = "table-impact composer item could not be found"
            LOGGER.exception(message)
            raise MapComposerError(message)
        impacts_html = composition.getComposerHtmlByItem(impact_table)
        if impacts_html is None:
            message = "Impacts QgsComposerHtml could not be found"
            LOGGER.exception(message)
            raise MapComposerError(message)
        impacts_html.setUrl(QUrl(self.population_html_path))

        # setup nearby table
        nearby_table = composition.getComposerItemById("table-nearby")
        if nearby_table is None:
            message = "table-nearby composer item could not be found"
            LOGGER.exception(message)
            raise MapComposerError(message)
        nearby_html = composition.getComposerHtmlByItem(nearby_table)
        if nearby_html is None:
            message = "Nearby QgsComposerHtml could not be found"
            LOGGER.exception(message)
            raise MapComposerError(message)
        nearby_html.setUrl(QUrl(self.nearby_html_path))

        # setup landcover table
        landcover_table = composition.getComposerItemById("table-landcover")
        if landcover_table is None:
            message = "table-landcover composer item could not be found"
            LOGGER.exception(message)
            raise MapComposerError(message)
        landcover_html = composition.getComposerHtmlByItem(landcover_table)
        if landcover_html is None:
            message = "Landcover QgsComposerHtml could not be found"
            LOGGER.exception(message)
            raise MapComposerError(message)
        landcover_html.setUrl(QUrl(self.landcover_html_path))

        # setup logos
        logos_id = ["logo-bnpb", "logo-geologi"]
        for logo_id in logos_id:
            logo_picture = composition.getComposerItemById(logo_id)
            if logo_picture is None:
                message = "%s composer item could not be found" % logo_id
                LOGGER.exception(message)
                raise MapComposerError(message)
            pic_path = os.path.basename(logo_picture.picturePath())
            pic_path = os.path.join("logo", pic_path)
            logo_picture.setPicturePath(self.ash_fixtures_dir(pic_path))

        # save a pdf
        composition.exportAsPDF(self.map_report_path)

        project_instance.write(QFileInfo(self.project_path))

        layer_registry.removeAllMapLayers()
        map_renderer.setDestinationCrs(default_crs)
        map_renderer.setProjectionsEnabled(False)
        LOGGER.info("Report generation completed.")
Exemple #6
0
    def __init__(
        self,
        event_time=None,
        volcano_name=None,
        volcano_location=None,
        eruption_height=None,
        region=None,
        alert_level=None,
        locale=None,
        working_dir=None,
        hazard_path=None,
        overview_path=None,
        highlight_base_path=None,
        population_path=None,
        volcano_path=None,
        landcover_path=None,
        cities_path=None,
        airport_path=None,
    ):
        """

        :param event_time:
        :param volcano_name:
        :param volcano_location:
        :param eruption_height:
        :param region:
        :param alert_level:
        :param locale:
        :param working_dir:
        :param hazard_path: It can be a url or local file path
        :param population_path:
        :param landcover_path:
        :param cities_path:
        :param airport_path:
        """
        QObject.__init__(self)
        if event_time:
            self.time = event_time
        else:
            self.time = datetime.datetime.now().replace(tzinfo=pytz.timezone("Asia/Jakarta"))

        # Check timezone awareness
        if not self.time.tzinfo:
            raise Exception("Need timezone aware object for event time")

        self.volcano_name = volcano_name
        self.volcano_location = volcano_location
        if self.volcano_location:
            self.longitude = self.volcano_location[0]
            self.latitude = self.volcano_location[1]
        else:
            self.longitude = None
            self.latitude = None
        self.erupction_height = eruption_height
        self.region = region
        self.alert_level = alert_level
        self.locale = locale
        if not self.locale:
            self.locale = "en"

        if not working_dir:
            raise Exception("Working directory can't be empty")
        self.working_dir = working_dir
        if not os.path.exists(self.working_dir_path()):
            os.makedirs(self.working_dir_path())
        # save hazard layer
        self.hazard_path = self.working_dir_path("hazard.tif")
        self.save_hazard_layer(hazard_path)

        if not os.path.exists(self.hazard_path):
            IOError("Hazard path doesn't exists")

        self.population_html_path = self.working_dir_path("population-table.html")
        self.nearby_html_path = self.working_dir_path("nearby-table.html")
        self.landcover_html_path = self.working_dir_path("landcover-table.html")
        self.map_report_path = self.working_dir_path("report.pdf")
        self.project_path = self.working_dir_path("project.qgs")
        self.impact_exists = None
        self.locale = "en"

        self.population_path = population_path
        self.cities_path = cities_path
        self.airport_path = airport_path
        self.landcover_path = landcover_path
        self.volcano_path = volcano_path
        self.highlight_base_path = highlight_base_path
        self.overview_path = overview_path

        # load layers
        self.hazard_layer = read_qgis_layer(self.hazard_path, "Ash Fall")
        self.population_layer = read_qgis_layer(self.population_path, "Population")
        self.landcover_layer = read_qgis_layer(self.landcover_path, "Landcover")
        self.cities_layer = read_qgis_layer(self.cities_path, "Cities")
        self.airport_layer = read_qgis_layer(self.airport_path, "Airport")
        self.volcano_layer = read_qgis_layer(self.volcano_path, "Volcano")
        self.highlight_base_layer = read_qgis_layer(self.highlight_base_path, "Base Map")
        self.overview_layer = read_qgis_layer(self.overview_path, "Overview")

        # Write metadata for self reference
        self.write_metadata()
Exemple #7
0
    def render_nearby_table(self):

        hazard_mapping = {0: "Very Low", 1: "Low", 2: "Moderate", 3: "High", 4: "Very High"}

        # load PLACES
        keyword_io = KeywordIO()
        try:
            cities_impact = read_qgis_layer(self.working_dir_path("cities_impact.shp"), "Cities")
            hazard = keyword_io.read_keywords(cities_impact, "target_field")
            hazard_field_index = cities_impact.fieldNameIndex(hazard)

            name_field = keyword_io.read_keywords(self.cities_layer, "name_field")
            name_field_index = cities_impact.fieldNameIndex(name_field)

            try:
                population_field = keyword_io.read_keywords(self.cities_layer, "population_field")
                population_field_index = cities_impact.fieldNameIndex(population_field)
            except KeywordNotFoundError:
                population_field = None
                population_field_index = None

            table_places = []
            for f in cities_impact.getFeatures():
                haz_class = f.attributes()[hazard_field_index]
                city_name = f.attributes()[name_field_index]
                if population_field_index >= 0:
                    city_pop = f.attributes()[population_field_index]
                else:
                    city_pop = 1
                # format:
                # [
                # 'hazard class',
                # 'city's population',
                # 'city's name',
                # 'the type'
                # ]
                haz = hazard_mapping[haz_class]
                item = {
                    "class": haz_class,
                    "hazard": haz,
                    "css": haz.lower().replace(" ", "-"),
                    "population": format_int(population_rounding(city_pop / 1000)),
                    "name": city_name.title(),
                    "type": "places",
                }
                table_places.append(item)

            # sort table by hazard zone, then population
            table_places = sorted(table_places, key=lambda x: (-x["class"], -x["population"]))
        except Exception as e:
            LOGGER.exception(e)
            table_places = []

        # load AIRPORTS
        try:
            airport_impact = read_qgis_layer(self.working_dir_path("airport_impact.shp"), "Airport")
            hazard = keyword_io.read_keywords(airport_impact, "target_field")
            hazard_field_index = airport_impact.fieldNameIndex(hazard)

            name_field = keyword_io.read_keywords(self.airport_layer, "name_field")
            name_field_index = airport_impact.fieldNameIndex(name_field)

            # airport doesnt have population, so enter 0 for population
            table_airports = []
            for f in airport_impact.getFeatures():
                haz_class = f.attributes()[hazard_field_index]
                airport_name = f.attributes()[name_field_index]
                haz = hazard_mapping[haz_class]
                item = {
                    "class": haz_class,
                    "hazard": haz,
                    "css": haz.lower().replace(" ", "-"),
                    "population": 0,
                    "name": airport_name.title(),
                    "type": "airport",
                }
                table_airports.append(item)

            # Sort by hazard class
            table_airports = sorted(table_airports, key=lambda x: -x["class"])
        except Exception as e:
            LOGGER.exception(e)
            table_airports = []

        # decide which to show
        # maximum 2 airport
        max_airports = 2
        airport_count = min(max_airports, len(table_airports))
        # maximum total 7 entries to show
        max_rows = 6
        places_count = min(len(table_places), max_rows - airport_count)

        # get top airport
        table_airports = table_airports[:airport_count]
        # get top places
        table_places = table_places[:places_count]

        item_list = table_places + table_airports

        # sort entry by hazard level
        item_list = sorted(item_list, key=lambda x: (-x["class"], -x["population"]))

        nearby_template = self.ash_fixtures_dir("nearby-table.template.html")
        with open(nearby_template) as f:
            template = Template(f.read())
            # generate table here
            html_string = template.render(item_list=item_list)

        with open(self.nearby_html_path, "w") as f:
            f.write(html_string)

        # copy airport logo
        shutil.copy(self.ash_fixtures_dir("logo/airport.jpg"), self.working_dir_path("airport.jpg"))
Exemple #8
0
    def generate_report(self):
        # Generate pdf report from impact/hazard
        if not self.impact_exists:
            # Cannot generate report when no impact layer present
            return

        project_path = os.path.join(self.report_path,
                                    'project-%s.qgs' % self.locale)
        project_instance = QgsProject.instance()
        project_instance.setFileName(project_path)
        project_instance.read()

        # Set up the map renderer that will be assigned to the composition
        map_renderer = CANVAS.mapRenderer()
        # Set the labelling engine for the canvas
        labelling_engine = QgsPalLabeling()
        map_renderer.setLabelingEngine(labelling_engine)

        # Enable on the fly CRS transformations
        map_renderer.setProjectionsEnabled(True)

        default_crs = map_renderer.destinationCrs()
        crs = QgsCoordinateReferenceSystem('EPSG:4326')
        map_renderer.setDestinationCrs(crs)

        # get layer registry
        layer_registry = QgsMapLayerRegistry.instance()
        layer_registry.removeAllMapLayers()
        # add impact layer
        population_affected_layer = read_qgis_layer(
            self.population_aggregate_path, self.tr('People Affected'))
        layer_registry.addMapLayer(population_affected_layer, True)
        # add boundary mask
        boundary_mask = read_qgis_layer(
            self.flood_fixtures_dir('boundary-mask.shp'))
        layer_registry.addMapLayer(boundary_mask, False)
        # add hazard layer
        hazard_layer = read_qgis_layer(self.hazard_path,
                                       self.tr('Flood Depth (cm)'))
        layer_registry.addMapLayer(hazard_layer, True)
        # add boundary layer
        boundary_layer = read_qgis_layer(
            self.flood_fixtures_dir('boundary-5.shp'))
        layer_registry.addMapLayer(boundary_layer, False)
        CANVAS.setExtent(boundary_layer.extent())
        CANVAS.refresh()
        # add basemap layer
        # this code uses OpenlayersPlugin
        base_map = QgsRasterLayer(self.flood_fixtures_dir('jakarta.jpg'))
        layer_registry.addMapLayer(base_map, False)
        CANVAS.refresh()

        template_path = self.flood_fixtures_dir('realtime-flood.qpt')

        with open(template_path) as f:
            template_content = f.read()

        document = QDomDocument()
        document.setContent(template_content)

        # set destination CRS to Jakarta CRS
        # EPSG:32748
        # This allows us to use the scalebar in meter unit scale
        crs = QgsCoordinateReferenceSystem('EPSG:32748')
        map_renderer.setDestinationCrs(crs)

        # Now set up the composition
        composition = QgsComposition(map_renderer)

        subtitution_map = self.event_dict()
        LOGGER.debug(subtitution_map)

        # load composition object from template
        result = composition.loadFromTemplate(document, subtitution_map)
        if not result:
            LOGGER.exception('Error loading template %s with keywords\n %s',
                             template_path, subtitution_map)
            raise MapComposerError

        # get main map canvas on the composition and set extent
        map_canvas = composition.getComposerItemById('map-canvas')
        if map_canvas:
            map_canvas.setNewExtent(map_canvas.currentMapExtent())
            map_canvas.renderModeUpdateCachedImage()
        else:
            LOGGER.exception('Map canvas could not be found in template %s',
                             template_path)
            raise MapComposerError

        # get map legend on the composition
        map_legend = composition.getComposerItemById('map-legend')
        if map_legend:
            # show only legend for Flood Depth
            # ''.star
            # showed_legend = [layer_id for layer_id in map_renderer.layerSet()
            #                  if layer_id.startswith('Flood_Depth')]
            # LOGGER.info(showed_legend)
            # LOGGER.info(map_renderer.layerSet())
            # LOGGER.info(hazard_layer.id())

            # map_legend.model().setLayerSet(showed_legend)
            # map_legend.modelV2().clear()
            # print dir(map_legend.modelV2())
            # map_legend.setLegendFilterByMapEnabled(True)

            map_legend.model().setLayerSet(
                [hazard_layer.id(),
                 population_affected_layer.id()])

        else:
            LOGGER.exception('Map legend could not be found in template %s',
                             template_path)
            raise MapComposerError

        content_analysis = composition.getComposerItemById(
            'content-analysis-result')
        if not content_analysis:
            message = 'Content analysis composer item could not be found'
            LOGGER.exception(message)
            raise MapComposerError(message)
        content_analysis_html = content_analysis.multiFrame()
        if content_analysis_html:
            # set url to generated html
            analysis_html_path = self.generate_analysis_result_html()
            # We're using manual HTML to avoid memory leak and segfault
            # happened when using Url Mode
            content_analysis_html.setContentMode(QgsComposerHtml.ManualHtml)
            with open(analysis_html_path) as f:
                content_analysis_html.setHtml(f.read())
                content_analysis_html.loadHtml()
        else:
            message = 'Content analysis HTML not found in template'
            LOGGER.exception(message)
            raise MapComposerError(message)

        # save a pdf
        composition.exportAsPDF(self.map_report_path)

        project_instance.write(QFileInfo(project_path))

        layer_registry.removeAllMapLayers()
        map_renderer.setDestinationCrs(default_crs)
        map_renderer.setProjectionsEnabled(False)
Exemple #9
0
    def generate_report(self):
        # Generate pdf report from impact/hazard
        if not self.impact_exists:
            # Cannot generate report when no impact layer present
            return

        project_path = os.path.join(
            self.report_path, 'project-%s.qgs' % self.locale)
        project_instance = QgsProject.instance()
        project_instance.setFileName(project_path)
        project_instance.read()

        # Set up the map renderer that will be assigned to the composition
        map_renderer = CANVAS.mapRenderer()
        # Set the labelling engine for the canvas
        labelling_engine = QgsPalLabeling()
        map_renderer.setLabelingEngine(labelling_engine)

        # Enable on the fly CRS transformations
        map_renderer.setProjectionsEnabled(True)

        default_crs = map_renderer.destinationCrs()
        crs = QgsCoordinateReferenceSystem('EPSG:4326')
        map_renderer.setDestinationCrs(crs)

        # get layer registry
        layer_registry = QgsMapLayerRegistry.instance()
        layer_registry.removeAllMapLayers()
        # add impact layer
        population_affected_layer = read_qgis_layer(
            self.population_aggregate_path, self.tr('People Affected'))
        layer_registry.addMapLayer(population_affected_layer, True)
        # add boundary mask
        boundary_mask = read_qgis_layer(
            self.flood_fixtures_dir('boundary-mask.shp'))
        layer_registry.addMapLayer(boundary_mask, False)
        # add hazard layer
        hazard_layer = read_qgis_layer(
            self.hazard_path, self.tr('Flood Depth (cm)'))
        layer_registry.addMapLayer(hazard_layer, True)
        # add boundary layer
        boundary_layer = read_qgis_layer(
            self.flood_fixtures_dir('boundary-5.shp'))
        layer_registry.addMapLayer(boundary_layer, False)
        CANVAS.setExtent(boundary_layer.extent())
        CANVAS.refresh()
        # add basemap layer
        # this code uses OpenlayersPlugin
        base_map = QgsRasterLayer(
            self.flood_fixtures_dir('jakarta.jpg'))
        layer_registry.addMapLayer(base_map, False)
        CANVAS.refresh()

        template_path = self.flood_fixtures_dir('realtime-flood.qpt')

        with open(template_path) as f:
            template_content = f.read()

        document = QDomDocument()
        document.setContent(template_content)

        # set destination CRS to Jakarta CRS
        # EPSG:32748
        # This allows us to use the scalebar in meter unit scale
        crs = QgsCoordinateReferenceSystem('EPSG:32748')
        map_renderer.setDestinationCrs(crs)

        # Now set up the composition
        composition = QgsComposition(map_renderer)

        subtitution_map = self.event_dict()
        LOGGER.debug(subtitution_map)

        # load composition object from template
        result = composition.loadFromTemplate(document, subtitution_map)
        if not result:
            LOGGER.exception(
                'Error loading template %s with keywords\n %s',
                template_path, subtitution_map)
            raise MapComposerError

        # get main map canvas on the composition and set extent
        map_canvas = composition.getComposerItemById('map-canvas')
        if map_canvas:
            map_canvas.setNewExtent(map_canvas.currentMapExtent())
            map_canvas.renderModeUpdateCachedImage()
        else:
            LOGGER.exception('Map canvas could not be found in template %s',
                             template_path)
            raise MapComposerError

        # get map legend on the composition
        map_legend = composition.getComposerItemById('map-legend')
        if map_legend:
            # show only legend for Flood Depth
            # ''.star
            # showed_legend = [layer_id for layer_id in map_renderer.layerSet()
            #                  if layer_id.startswith('Flood_Depth')]
            # LOGGER.info(showed_legend)
            # LOGGER.info(map_renderer.layerSet())
            # LOGGER.info(hazard_layer.id())

            # map_legend.model().setLayerSet(showed_legend)
            # map_legend.modelV2().clear()
            # print dir(map_legend.modelV2())
            # map_legend.setLegendFilterByMapEnabled(True)

            map_legend.model().setLayerSet(
                [hazard_layer.id(), population_affected_layer.id()])

        else:
            LOGGER.exception('Map legend could not be found in template %s',
                             template_path)
            raise MapComposerError

        content_analysis = composition.getComposerItemById(
            'content-analysis-result')
        if not content_analysis:
            message = 'Content analysis composer item could not be found'
            LOGGER.exception(message)
            raise MapComposerError(message)
        content_analysis_html = content_analysis.multiFrame()
        if content_analysis_html:
            # set url to generated html
            analysis_html_path = self.generate_analysis_result_html()
            # We're using manual HTML to avoid memory leak and segfault
            # happened when using Url Mode
            content_analysis_html.setContentMode(QgsComposerHtml.ManualHtml)
            with open(analysis_html_path) as f:
                content_analysis_html.setHtml(f.read())
                content_analysis_html.loadHtml()
        else:
            message = 'Content analysis HTML not found in template'
            LOGGER.exception(message)
            raise MapComposerError(message)

        # save a pdf
        composition.exportAsPDF(self.map_report_path)

        project_instance.write(QFileInfo(project_path))

        layer_registry.removeAllMapLayers()
        map_renderer.setDestinationCrs(default_crs)
        map_renderer.setProjectionsEnabled(False)
Exemple #10
0
    def generate_population_aggregation(self):

        # duplicate exposure data
        QgsVectorFileWriter.writeAsVectorFormat(
            self.exposure_layer.as_qgis_native(),
            self.population_aggregate_path,
            'CP1250',
            None,
            'ESRI Shapefile')
        population_aggregate = read_qgis_layer(
            self.population_aggregate_path, 'Impacted Population')
        shutil.copy(
            self.population_path.replace('.shp', '.xml'),
            self.population_aggregate_path.replace('.shp', '.xml'))

        # add affected population field
        population_aggregate.startEditing()
        field = QgsField(self.affect_field, QVariant.Int)
        field2 = QgsField(self.target_field, QVariant.Int)
        population_aggregate.dataProvider().addAttributes([field, field2])
        population_aggregate.commitChanges()

        idx = population_aggregate.fieldNameIndex(self.affect_field)
        idx2 = population_aggregate.fieldNameIndex(self.target_field)
        impact_layer = self.impact_layer.as_qgis_native()
        keyword_io = KeywordIO()
        name_field = keyword_io.read_keywords(
            population_aggregate, 'area_name_field')
        attribute_field = keyword_io.read_keywords(
            population_aggregate, 'field')

        # calculate affected data
        district_dict = {}
        for f in impact_layer.getFeatures():
            if f[self.target_field] >= 1:
                if f[name_field] in district_dict:
                    district_dict[f[name_field]] += f[attribute_field]
                else:
                    district_dict[f[name_field]] = f[attribute_field]

        population_aggregate.startEditing()
        for f in population_aggregate.getFeatures():
            f[idx] = district_dict.get(f[name_field], 0)
            if f[idx] == 0:
                # mark as unaffected
                f[idx2] = 0
            else:
                # mark as affected
                f[idx2] = 1

            population_aggregate.updateFeature(f)
        population_aggregate.commitChanges()
        self.affected_aggregate = district_dict

        # calculate total affected people
        total_affected = 0
        for k, v in district_dict.iteritems():
            total_affected += v

        # calculate new minimum needs
        min_needs = self.impact_data.minimum_needs
        for k, v in min_needs.iteritems():
            for need in v:
                need['amount'] = need['value'] * total_affected
Exemple #11
0
    def generate_report(self):
        # Generate pdf report from impact/hazard
        LOGGER.info('Generating report')
        if not self.impact_exists:
            # Cannot generate report when no impact layer present
            LOGGER.info('Cannot Generate report when no impact present.')
            return

        project_instance = QgsProject.instance()
        project_instance.setFileName(self.project_path)
        project_instance.read()

        # get layer registry
        layer_registry = QgsMapLayerRegistry.instance()
        layer_registry.removeAllMapLayers()

        # Set up the map renderer that will be assigned to the composition
        map_renderer = CANVAS.mapRenderer()

        # Enable on the fly CRS transformations
        map_renderer.setProjectionsEnabled(True)

        default_crs = map_renderer.destinationCrs()
        crs = QgsCoordinateReferenceSystem('EPSG:4326')
        map_renderer.setDestinationCrs(crs)

        # add place name layer
        layer_registry.addMapLayer(self.cities_layer, False)

        # add airport layer
        layer_registry.addMapLayer(self.airport_layer, False)

        # add volcano layer
        layer_registry.addMapLayer(self.volcano_layer, False)

        # add impact layer
        hazard_layer = read_qgis_layer(self.hazard_path,
                                       self.tr('People Affected'))
        layer_registry.addMapLayer(hazard_layer, False)

        # add basemap layer
        layer_registry.addMapLayer(self.highlight_base_layer, False)

        # add basemap layer
        layer_registry.addMapLayer(self.overview_layer, False)

        CANVAS.setExtent(hazard_layer.extent())
        CANVAS.refresh()

        template_path = self.ash_fixtures_dir('realtime-ash.qpt')

        with open(template_path) as f:
            template_content = f.read()

        document = QDomDocument()
        document.setContent(template_content)

        # Now set up the composition
        # map_settings = QgsMapSettings()
        # composition = QgsComposition(map_settings)
        composition = QgsComposition(map_renderer)

        subtitution_map = self.event_dict()
        LOGGER.debug(subtitution_map)

        # load composition object from template
        result = composition.loadFromTemplate(document, subtitution_map)
        if not result:
            LOGGER.exception('Error loading template %s with keywords\n %s',
                             template_path, subtitution_map)
            raise MapComposerError

        # get main map canvas on the composition and set extent
        map_impact = composition.getComposerItemById('map-impact')
        if map_impact:
            map_impact.zoomToExtent(hazard_layer.extent())
            map_impact.renderModeUpdateCachedImage()
        else:
            LOGGER.exception('Map canvas could not be found in template %s',
                             template_path)
            raise MapComposerError

        # get overview map canvas on the composition and set extent
        map_overall = composition.getComposerItemById('map-overall')
        if map_overall:
            map_overall.setLayerSet([self.overview_layer.id()])
            # this is indonesia extent
            indonesia_extent = QgsRectangle(94.0927980005593554,
                                            -15.6629591962689343,
                                            142.0261493318861312,
                                            10.7379406374101816)
            map_overall.zoomToExtent(indonesia_extent)
            map_overall.renderModeUpdateCachedImage()
        else:
            LOGGER.exception('Map canvas could not be found in template %s',
                             template_path)
            raise MapComposerError

        # setup impact table
        self.render_population_table()
        self.render_nearby_table()
        self.render_landcover_table()

        impact_table = composition.getComposerItemById('table-impact')
        if impact_table is None:
            message = 'table-impact composer item could not be found'
            LOGGER.exception(message)
            raise MapComposerError(message)
        impacts_html = composition.getComposerHtmlByItem(impact_table)
        if impacts_html is None:
            message = 'Impacts QgsComposerHtml could not be found'
            LOGGER.exception(message)
            raise MapComposerError(message)
        impacts_html.setUrl(QUrl(self.population_html_path))

        # setup nearby table
        nearby_table = composition.getComposerItemById('table-nearby')
        if nearby_table is None:
            message = 'table-nearby composer item could not be found'
            LOGGER.exception(message)
            raise MapComposerError(message)
        nearby_html = composition.getComposerHtmlByItem(nearby_table)
        if nearby_html is None:
            message = 'Nearby QgsComposerHtml could not be found'
            LOGGER.exception(message)
            raise MapComposerError(message)
        nearby_html.setUrl(QUrl(self.nearby_html_path))

        # setup landcover table
        landcover_table = composition.getComposerItemById('table-landcover')
        if landcover_table is None:
            message = 'table-landcover composer item could not be found'
            LOGGER.exception(message)
            raise MapComposerError(message)
        landcover_html = composition.getComposerHtmlByItem(landcover_table)
        if landcover_html is None:
            message = 'Landcover QgsComposerHtml could not be found'
            LOGGER.exception(message)
            raise MapComposerError(message)
        landcover_html.setUrl(QUrl(self.landcover_html_path))

        # setup logos
        logos_id = ['logo-bnpb', 'logo-geologi']
        for logo_id in logos_id:
            logo_picture = composition.getComposerItemById(logo_id)
            if logo_picture is None:
                message = '%s composer item could not be found' % logo_id
                LOGGER.exception(message)
                raise MapComposerError(message)
            pic_path = os.path.basename(logo_picture.picturePath())
            pic_path = os.path.join('logo', pic_path)
            logo_picture.setPicturePath(self.ash_fixtures_dir(pic_path))

        # save a pdf
        composition.exportAsPDF(self.map_report_path)

        project_instance.write(QFileInfo(self.project_path))

        layer_registry.removeAllMapLayers()
        map_renderer.setDestinationCrs(default_crs)
        map_renderer.setProjectionsEnabled(False)
        LOGGER.info('Report generation completed.')
Exemple #12
0
    def __init__(self,
                 event_time=None,
                 volcano_name=None,
                 volcano_location=None,
                 eruption_height=None,
                 region=None,
                 alert_level=None,
                 locale=None,
                 working_dir=None,
                 hazard_path=None,
                 overview_path=None,
                 highlight_base_path=None,
                 population_path=None,
                 volcano_path=None,
                 landcover_path=None,
                 cities_path=None,
                 airport_path=None):
        """

        :param event_time:
        :param volcano_name:
        :param volcano_location:
        :param eruption_height:
        :param region:
        :param alert_level:
        :param locale:
        :param working_dir:
        :param hazard_path: It can be a url or local file path
        :param population_path:
        :param landcover_path:
        :param cities_path:
        :param airport_path:
        """
        QObject.__init__(self)
        if event_time:
            self.time = event_time
        else:
            self.time = datetime.datetime.now().replace(
                tzinfo=pytz.timezone('Asia/Jakarta'))

        # Check timezone awareness
        if not self.time.tzinfo:
            raise Exception('Need timezone aware object for event time')

        self.volcano_name = volcano_name
        self.volcano_location = volcano_location
        if self.volcano_location:
            self.longitude = self.volcano_location[0]
            self.latitude = self.volcano_location[1]
        else:
            self.longitude = None
            self.latitude = None
        self.erupction_height = eruption_height
        self.region = region
        self.alert_level = alert_level
        self.locale = locale
        if not self.locale:
            self.locale = 'en'

        if not working_dir:
            raise Exception("Working directory can't be empty")
        self.working_dir = working_dir
        if not os.path.exists(self.working_dir_path()):
            os.makedirs(self.working_dir_path())
        # save hazard layer
        self.hazard_path = self.working_dir_path('hazard.tif')
        self.save_hazard_layer(hazard_path)

        if not os.path.exists(self.hazard_path):
            IOError("Hazard path doesn't exists")

        self.population_html_path = self.working_dir_path(
            'population-table.html')
        self.nearby_html_path = self.working_dir_path('nearby-table.html')
        self.landcover_html_path = self.working_dir_path(
            'landcover-table.html')
        self.map_report_path = self.working_dir_path('report.pdf')
        self.project_path = self.working_dir_path('project.qgs')
        self.impact_exists = None
        self.locale = 'en'

        self.population_path = population_path
        self.cities_path = cities_path
        self.airport_path = airport_path
        self.landcover_path = landcover_path
        self.volcano_path = volcano_path
        self.highlight_base_path = highlight_base_path
        self.overview_path = overview_path

        # load layers
        self.hazard_layer = read_qgis_layer(self.hazard_path, 'Ash Fall')
        self.population_layer = read_qgis_layer(self.population_path,
                                                'Population')
        self.landcover_layer = read_qgis_layer(self.landcover_path,
                                               'Landcover')
        self.cities_layer = read_qgis_layer(self.cities_path, 'Cities')
        self.airport_layer = read_qgis_layer(self.airport_path, 'Airport')
        self.volcano_layer = read_qgis_layer(self.volcano_path, 'Volcano')
        self.highlight_base_layer = read_qgis_layer(self.highlight_base_path,
                                                    'Base Map')
        self.overview_layer = read_qgis_layer(self.overview_path, 'Overview')

        # Write metadata for self reference
        self.write_metadata()
Exemple #13
0
    def render_nearby_table(self):

        hazard_mapping = {
            0: 'Very Low',
            1: 'Low',
            2: 'Moderate',
            3: 'High',
            4: 'Very High'
        }

        # load PLACES
        keyword_io = KeywordIO()
        try:
            cities_impact = read_qgis_layer(
                self.working_dir_path('cities_impact.shp'), 'Cities')
            hazard = keyword_io.read_keywords(cities_impact, 'target_field')
            hazard_field_index = cities_impact.fieldNameIndex(hazard)

            name_field = keyword_io.read_keywords(self.cities_layer,
                                                  'name_field')
            name_field_index = cities_impact.fieldNameIndex(name_field)

            try:
                population_field = keyword_io.read_keywords(
                    self.cities_layer, 'population_field')
                population_field_index = cities_impact.fieldNameIndex(
                    population_field)
            except KeywordNotFoundError:
                population_field = None
                population_field_index = None

            table_places = []
            for f in cities_impact.getFeatures():
                haz_class = f.attributes()[hazard_field_index]
                city_name = f.attributes()[name_field_index]
                if population_field_index >= 0:
                    city_pop = f.attributes()[population_field_index]
                else:
                    city_pop = 1
                # format:
                # [
                # 'hazard class',
                # 'city's population',
                # 'city's name',
                # 'the type'
                # ]
                haz = hazard_mapping[haz_class]
                item = {
                    'class': haz_class,
                    'hazard': haz,
                    'css': haz.lower().replace(' ', '-'),
                    'population':
                    format_int(population_rounding(city_pop / 1000)),
                    'name': city_name.title(),
                    'type': 'places'
                }
                table_places.append(item)

            # sort table by hazard zone, then population
            table_places = sorted(table_places,
                                  key=lambda x:
                                  (-x['class'], -x['population']))
        except Exception as e:
            LOGGER.exception(e)
            table_places = []

        # load AIRPORTS
        try:
            airport_impact = read_qgis_layer(
                self.working_dir_path('airport_impact.shp'), 'Airport')
            hazard = keyword_io.read_keywords(airport_impact, 'target_field')
            hazard_field_index = airport_impact.fieldNameIndex(hazard)

            name_field = keyword_io.read_keywords(self.airport_layer,
                                                  'name_field')
            name_field_index = airport_impact.fieldNameIndex(name_field)

            # airport doesnt have population, so enter 0 for population
            table_airports = []
            for f in airport_impact.getFeatures():
                haz_class = f.attributes()[hazard_field_index]
                airport_name = f.attributes()[name_field_index]
                haz = hazard_mapping[haz_class]
                item = {
                    'class': haz_class,
                    'hazard': haz,
                    'css': haz.lower().replace(' ', '-'),
                    'population': 0,
                    'name': airport_name.title(),
                    'type': 'airport'
                }
                table_airports.append(item)

            # Sort by hazard class
            table_airports = sorted(table_airports, key=lambda x: -x['class'])
        except Exception as e:
            LOGGER.exception(e)
            table_airports = []

        # decide which to show
        # maximum 2 airport
        max_airports = 2
        airport_count = min(max_airports, len(table_airports))
        # maximum total 7 entries to show
        max_rows = 6
        places_count = min(len(table_places), max_rows - airport_count)

        # get top airport
        table_airports = table_airports[:airport_count]
        # get top places
        table_places = table_places[:places_count]

        item_list = table_places + table_airports

        # sort entry by hazard level
        item_list = sorted(item_list,
                           key=lambda x: (-x['class'], -x['population']))

        nearby_template = self.ash_fixtures_dir('nearby-table.template.html')
        with open(nearby_template) as f:
            template = Template(f.read())
            # generate table here
            html_string = template.render(item_list=item_list)

        with open(self.nearby_html_path, 'w') as f:
            f.write(html_string)

        # copy airport logo
        shutil.copy(self.ash_fixtures_dir('logo/airport.jpg'),
                    self.working_dir_path('airport.jpg'))