def test_impact_layer_attribution(self): """Test we get an attribution html snippet nicely for impact layers.""" keywords = { 'hazard_title': 'Sample Hazard Title', 'hazard_source': 'Sample Hazard Source', 'exposure_title': 'Sample Exposure Title', 'exposure_source': 'Sample Exposure Source'} attribution = impact_attribution(keywords) print attribution # noinspection PyArgumentList self.assertEqual(len(attribution.to_text()), 170)
def test_impact_layer_attribution(self): """Test we get an attribution html snippet nicely for impact layers.""" keywords = { 'hazard_title': 'Sample Hazard Title', 'hazard_source': 'Sample Hazard Source', 'exposure_title': 'Sample Exposure Title', 'exposure_source': 'Sample Exposure Source' } attribution = impact_attribution(keywords) print attribution # noinspection PyArgumentList self.assertEqual(len(attribution.to_text()), 170)
def test_localised_attribution(self): """Test we can localise attribution.""" os.environ['LANG'] = 'id' keywords = { 'hazard_title': 'Jakarta 2007 flood', 'hazard_source': 'Sample Hazard Source', 'exposure_title': 'People in Jakarta', 'exposure_source': 'Sample Exposure Source'} html = impact_attribution(keywords, True) assert html == '11' # Set back to en os.environ['LANG'] = 'en'
def test_impact_layer_attribution(self): """Test we get an attribution html snippet nicely for impact layers.""" keywords = { "hazard_title": "Sample Hazard Title", "hazard_source": "Sample Hazard Source", "exposure_title": "Sample Exposure Title", "exposure_source": "Sample Exposure Source", } attribution = impact_attribution(keywords) control_file_path = test_data_path("control", "files", "impact-layer-attribution.txt") expected_result = codecs.open(control_file_path, mode="r", encoding="utf-8").readlines() for line in expected_result: line = line.replace("\n", "") self.assertIn(line, attribution.to_text())
def test_localised_attribution(self): """Test we can localise attribution.""" os.environ["LANG"] = "id" keywords = { "hazard_title": "Jakarta 2007 flood", "hazard_source": "Sample Hazard Source", "exposure_title": "People in Jakarta", "exposure_source": "Sample Exposure Source", } html = impact_attribution(keywords, True) print html assert html == "11" # Set back to en os.environ["LANG"] = "en"
def test_impact_layer_attribution(self): """Test we get an attribution html snippet nicely for impact layers.""" keywords = { 'hazard_title': 'Sample Hazard Title', 'hazard_source': 'Sample Hazard Source', 'exposure_title': 'Sample Exposure Title', 'exposure_source': 'Sample Exposure Source' } attribution = impact_attribution(keywords) control_file_path = test_data_path('control', 'files', 'impact-layer-attribution.txt') expected_result = codecs.open(control_file_path, mode='r', encoding='utf-8').readlines() for line in expected_result: line = line.replace('\n', '') self.assertIn(line, attribution.to_text())
def test_impact_layer_attribution(self): """Test we get an attribution html snippet nicely for impact layers.""" keywords = { 'hazard_title': 'Sample Hazard Title', 'hazard_source': 'Sample Hazard Source', 'exposure_title': 'Sample Exposure Title', 'exposure_source': 'Sample Exposure Source'} attribution = impact_attribution(keywords) control_file_path = test_data_path( 'control', 'files', 'impact-layer-attribution.txt') expected_result = codecs.open( control_file_path, mode='r', encoding='utf-8').readlines() for line in expected_result: line = line.replace('\n', '') self.assertIn(line, attribution.to_text())
def show_results(self, qgis_impact_layer, engine_impact_layer): """Helper function for slot activated when the process is done. .. note:: Adapted from the dock :param qgis_impact_layer: A QGIS layer representing the impact. :type qgis_impact_layer: QgsMapLayer, QgsVectorLayer, QgsRasterLayer :param engine_impact_layer: A safe_layer representing the impact. :type engine_impact_layer: ReadLayer :returns: Provides a report for writing to the dock. :rtype: str """ keywords = self.keyword_io.read_keywords(qgis_impact_layer) # write postprocessing report to keyword output = self.analysis.postprocessor_manager.get_output( self.analysis.aggregator.aoi_mode) keywords['postprocessing_report'] = output.to_html( suppress_newlines=True) self.keyword_io.write_keywords(qgis_impact_layer, keywords) # Get tabular information from impact layer report = m.Message() report.add(LOGO_ELEMENT) report.add(m.Heading(self.tr( 'Analysis Results'), **INFO_STYLE)) report.add(self.keyword_io.read_keywords( qgis_impact_layer, 'impact_summary')) # Get requested style for impact layer of either kind style = engine_impact_layer.get_style_info() style_type = engine_impact_layer.get_style_type() # Determine styling for QGIS layer if engine_impact_layer.is_vector: LOGGER.debug('myEngineImpactLayer.is_vector') if not style: # Set default style if possible pass elif style_type == 'categorizedSymbol': LOGGER.debug('use categorized') set_vector_categorized_style(qgis_impact_layer, style) elif style_type == 'graduatedSymbol': LOGGER.debug('use graduated') set_vector_graduated_style(qgis_impact_layer, style) elif engine_impact_layer.is_raster: LOGGER.debug('myEngineImpactLayer.is_raster') if not style: qgis_impact_layer.setDrawingStyle("SingleBandPseudoColor") # qgis_impact_layer.setColorShadingAlgorithm( # QgsRasterLayer.PseudoColorShader) else: setRasterStyle(qgis_impact_layer, style) else: message = self.tr( 'Impact layer %s was neither a raster or a vector layer') % ( qgis_impact_layer.source()) # noinspection PyExceptionInherit raise ReadLayerError(message) # Add layers to QGIS layers_to_add = [] if self.show_intermediate_layers: layers_to_add.append(self.analysis.aggregator.layer) layers_to_add.append(qgis_impact_layer) QgsMapLayerRegistry.instance().addMapLayers(layers_to_add) # make sure it is active in the legend - needed since QGIS 2.4 self.iface.setActiveLayer(qgis_impact_layer) # then zoom to it if self.zoom_to_impact_flag: self.iface.zoomToActiveLayer() if self.hide_exposure_flag: exposure_layer = self.analysis.exposure_layer legend = self.iface.legendInterface() legend.setLayerVisible(exposure_layer, False) # append postprocessing report report.add(output.to_html()) # Layer attribution comes last report.add(impact_attribution(keywords).to_html(True)) # Return text to display in report panel return report
def show_results(self): """Helper function for slot activated when the process is done. .. versionchanged:: 3.4 - removed parameters. .. note:: If you update this function, please report your change to safe.gui.widgets.dock.show_results too. :returns: Provides a report for writing to the dock. :rtype: str """ qgis_exposure = self.impact_function.exposure.qgis_layer() qgis_hazard = self.impact_function.hazard.qgis_layer() qgis_aggregation = self.impact_function.aggregation.qgis_layer() safe_impact_layer = self.impact_function.impact qgis_impact_layer = read_impact_layer(safe_impact_layer) keywords = self.keyword_io.read_keywords(qgis_impact_layer) json_path = os.path.splitext(qgis_impact_layer.source())[0] + '.json' # write postprocessing report to keyword postprocessor_data = self.impact_function.postprocessor_manager.\ get_json_data(self.impact_function.aggregator.aoi_mode) post_processing_report = m.Message() if os.path.exists(json_path): with open(json_path) as json_file: impact_data = json.load( json_file, object_pairs_hook=OrderedDict) impact_data['post processing'] = postprocessor_data with open(json_path, 'w') as json_file_2: json.dump(impact_data, json_file_2, indent=2) else: post_processing_report = self.impact_function.\ postprocessor_manager.get_output( self.impact_function.aggregator.aoi_mode) keywords['postprocessing_report'] = post_processing_report.to_html( suppress_newlines=True) self.keyword_io.write_keywords(qgis_impact_layer, keywords) # Get tabular information from impact layer report = m.Message() report.add(LOGO_ELEMENT) report.add(m.Heading(self.tr('Analysis Results'), **INFO_STYLE)) # If JSON Impact Data Exist, use JSON json_path = qgis_impact_layer.source()[:-3] + 'json' LOGGER.debug('JSON Path %s' % json_path) if os.path.exists(json_path): impact_template = get_report_template(json_file=json_path) impact_report = impact_template.generate_message_report() report.add(impact_report) else: report.add(self.keyword_io.read_keywords( qgis_impact_layer, 'impact_summary')) # append postprocessing report report.add(post_processing_report.to_html()) # Layer attribution comes last report.add(impact_attribution(keywords).to_html(True)) # Get requested style for impact layer of either kind style = safe_impact_layer.get_style_info() style_type = safe_impact_layer.get_style_type() # Determine styling for QGIS layer if safe_impact_layer.is_vector: if not style: # Set default style if possible pass elif style_type == 'categorizedSymbol': LOGGER.debug('use categorized') set_vector_categorized_style(qgis_impact_layer, style) elif style_type == 'graduatedSymbol': LOGGER.debug('use graduated') set_vector_graduated_style(qgis_impact_layer, style) elif safe_impact_layer.is_raster: if not style: qgis_impact_layer.setDrawingStyle("SingleBandPseudoColor") else: setRasterStyle(qgis_impact_layer, style) else: message = self.tr( 'Impact layer %s was neither a raster or a vector layer') % ( qgis_impact_layer.source()) # noinspection PyExceptionInherit raise ReadLayerError(message) legend = self.iface.legendInterface() # Insert the aggregation output above the input aggregation layer if self.show_intermediate_layers: add_above_layer( self.impact_function.aggregator.layer, qgis_aggregation) legend.setLayerVisible(self.impact_function.aggregator.layer, True) if self.hide_exposure_flag: # Insert the impact always above the hazard add_above_layer( qgis_impact_layer, qgis_hazard) else: # Insert the impact above the hazard and the exposure if # we don't hide the exposure. See #2899 add_above_layer( qgis_impact_layer, qgis_exposure, qgis_hazard) # In QGIS 2.14.2 and GDAL 1.11.3, if the exposure is in 3857, # the impact layer is in 54004, we need to change it. See issue #2790. if qgis_exposure.crs().authid() == 'EPSG:3857': if qgis_impact_layer.crs().authid() != 'EPSG:3857': epsg_3857 = QgsCoordinateReferenceSystem(3857) qgis_impact_layer.setCrs(epsg_3857) # make sure it is active in the legend - needed since QGIS 2.4 self.iface.setActiveLayer(qgis_impact_layer) # then zoom to it if self.zoom_to_impact_flag: self.iface.zoomToActiveLayer() if self.hide_exposure_flag: exposure_layer = self.get_exposure_layer() legend.setLayerVisible(exposure_layer, False) # Make the layer visible. Might be hidden by default. See #2925 legend.setLayerVisible(qgis_impact_layer, True) # Return text to display in report panel return report
def print_impact_table(self, output_path): """Pint summary from impact layer to PDF. ..note:: The order of the report: 1. Summary table 2. Aggregation table 3. Attribution table :param output_path: Output path. :type output_path: str :return: Path to generated pdf file. :rtype: str :raises: None """ keywords = self._keyword_io.read_keywords(self.layer) if output_path is None: output_path = unique_filename(suffix='.pdf', dir=temp_dir()) summary_table = keywords.get('impact_summary', None) full_table = keywords.get('impact_table', None) aggregation_table = keywords.get('postprocessing_report', None) attribution_table = impact_attribution(keywords) # (AG) We will not use impact_table as most of the IF use that as: # impact_table = impact_summary + some information intended to be # shown on screen (see FloodOsmBuilding) # Unless the impact_summary is None, we will use impact_table as the # alternative html = LOGO_ELEMENT.to_html() html += m.Heading(tr('Analysis Results'), **INFO_STYLE).to_html() if summary_table is None: html += full_table else: html += summary_table if aggregation_table is not None: html += aggregation_table if attribution_table is not None: html += attribution_table.to_html() html = html_header() + html + html_footer() # Print HTML using composition # For QGIS < 2.4 compatibility # QgsMapSettings is added in 2.4 if qgis_version() < 20400: map_settings = QgsMapRenderer() else: map_settings = QgsMapSettings() # A4 Portrait paper_width = 210 paper_height = 297 # noinspection PyCallingNonCallable composition = QgsComposition(map_settings) # noinspection PyUnresolvedReferences composition.setPlotStyle(QgsComposition.Print) composition.setPaperSize(paper_width, paper_height) composition.setPrintResolution(300) # Add HTML Frame # noinspection PyCallingNonCallable html_item = QgsComposerHtml(composition, False) margin_left = 10 margin_top = 10 # noinspection PyCallingNonCallable html_frame = QgsComposerFrame( composition, html_item, margin_left, margin_top, paper_width - 2 * margin_left, paper_height - 2 * margin_top) html_item.addFrame(html_frame) # Set HTML # From QGIS 2.6, we can set composer HTML with manual HTML if qgis_version() < 20600: html_path = unique_filename( prefix='report', suffix='.html', dir=temp_dir()) html_to_file(html, file_path=html_path) html_url = QUrl.fromLocalFile(html_path) html_item.setUrl(html_url) else: # noinspection PyUnresolvedReferences html_item.setContentMode(QgsComposerHtml.ManualHtml) # noinspection PyUnresolvedReferences html_item.setResizeMode(QgsComposerHtml.RepeatUntilFinished) html_item.setHtml(html) html_item.loadHtml() composition.exportAsPDF(output_path) return output_path
def show_results(self, qgis_impact_layer, engine_impact_layer): """Helper function for slot activated when the process is done. .. note:: Adapted from the dock :param qgis_impact_layer: A QGIS layer representing the impact. :type qgis_impact_layer: QgsMapLayer, QgsVectorLayer, QgsRasterLayer :param engine_impact_layer: A safe_layer representing the impact. :type engine_impact_layer: ReadLayer :returns: Provides a report for writing to the dock. :rtype: str """ keywords = self.keyword_io.read_keywords(qgis_impact_layer) # write postprocessing report to keyword output = self.analysis.postprocessor_manager.get_output( self.analysis.aggregator.aoi_mode) keywords['postprocessing_report'] = output.to_html( suppress_newlines=True) self.keyword_io.write_keywords(qgis_impact_layer, keywords) # Get tabular information from impact layer report = m.Message() report.add(LOGO_ELEMENT) report.add(m.Heading(self.tr('Analysis Results'), **INFO_STYLE)) report.add( self.keyword_io.read_keywords(qgis_impact_layer, 'impact_summary')) # Get requested style for impact layer of either kind style = engine_impact_layer.get_style_info() style_type = engine_impact_layer.get_style_type() # Determine styling for QGIS layer if engine_impact_layer.is_vector: LOGGER.debug('myEngineImpactLayer.is_vector') if not style: # Set default style if possible pass elif style_type == 'categorizedSymbol': LOGGER.debug('use categorized') set_vector_categorized_style(qgis_impact_layer, style) elif style_type == 'graduatedSymbol': LOGGER.debug('use graduated') set_vector_graduated_style(qgis_impact_layer, style) elif engine_impact_layer.is_raster: LOGGER.debug('myEngineImpactLayer.is_raster') if not style: qgis_impact_layer.setDrawingStyle("SingleBandPseudoColor") # qgis_impact_layer.setColorShadingAlgorithm( # QgsRasterLayer.PseudoColorShader) else: setRasterStyle(qgis_impact_layer, style) else: message = self.tr( 'Impact layer %s was neither a raster or a vector layer') % ( qgis_impact_layer.source()) # noinspection PyExceptionInherit raise ReadLayerError(message) # Add layers to QGIS layers_to_add = [] if self.show_intermediate_layers: layers_to_add.append(self.analysis.aggregator.layer) layers_to_add.append(qgis_impact_layer) QgsMapLayerRegistry.instance().addMapLayers(layers_to_add) # make sure it is active in the legend - needed since QGIS 2.4 self.iface.setActiveLayer(qgis_impact_layer) # then zoom to it if self.zoom_to_impact_flag: self.iface.zoomToActiveLayer() if self.hide_exposure_flag: exposure_layer = self.parent.get_exposure_layer() legend = self.iface.legendInterface() legend.setLayerVisible(exposure_layer, False) # append postprocessing report report.add(output.to_html()) # Layer attribution comes last report.add(impact_attribution(keywords).to_html(True)) # Return text to display in report panel return report
def print_impact_table(self, output_path): """Pint summary from impact layer to PDF. ..note:: The order of the report: 1. Summary table 2. Aggregation table 3. Attribution table :param output_path: Output path. :type output_path: str :return: Path to generated pdf file. :rtype: str :raises: None """ keywords = self._keyword_io.read_keywords(self.layer) if output_path is None: output_path = unique_filename(suffix='.pdf', dir=temp_dir()) try: impact_template = get_report_template(self.layer.source()) summary_table = impact_template.generate_html_report() except: summary_table = keywords.get('impact_summary', None) full_table = keywords.get('impact_table', None) aggregation_table = keywords.get('postprocessing_report', None) attribution_table = impact_attribution(keywords) # (AG) We will not use impact_table as most of the IF use that as: # impact_table = impact_summary + some information intended to be # shown on screen (see FloodOsmBuilding) # Unless the impact_summary is None, we will use impact_table as the # alternative html = m.Brand().to_html() html += m.Heading(tr('Analysis Results'), **INFO_STYLE).to_html() if summary_table is None: html += full_table else: html += summary_table if aggregation_table is not None: html += aggregation_table if attribution_table is not None: html += attribution_table.to_html() html = html_header() + html + html_footer() # Print HTML using composition # For QGIS < 2.4 compatibility # QgsMapSettings is added in 2.4 if qgis_version() < 20400: map_settings = QgsMapRenderer() else: map_settings = QgsMapSettings() # A4 Portrait # TODO: Will break when we try to use larger print layouts TS paper_width = 210 paper_height = 297 # noinspection PyCallingNonCallable composition = QgsComposition(map_settings) # noinspection PyUnresolvedReferences composition.setPlotStyle(QgsComposition.Print) composition.setPaperSize(paper_width, paper_height) composition.setPrintResolution(300) # Add HTML Frame # noinspection PyCallingNonCallable html_item = QgsComposerHtml(composition, False) margin_left = 10 margin_top = 10 # noinspection PyCallingNonCallable html_frame = QgsComposerFrame( composition, html_item, margin_left, margin_top, paper_width - 2 * margin_left, paper_height - 2 * margin_top) html_item.addFrame(html_frame) # Set HTML # From QGIS 2.6, we can set composer HTML with manual HTML if qgis_version() < 20600: html_path = unique_filename( prefix='report', suffix='.html', dir=temp_dir()) html_to_file(html, file_path=html_path) html_url = QUrl.fromLocalFile(html_path) html_item.setUrl(html_url) else: # noinspection PyUnresolvedReferences html_item.setContentMode(QgsComposerHtml.ManualHtml) # noinspection PyUnresolvedReferences html_item.setResizeMode(QgsComposerHtml.RepeatUntilFinished) html_item.setHtml(html) # RMN: This line below breaks in InaSAFE Headless after one # successful call. This is because the function is not # thread safe. Can't do anything about this, so avoid calling this # function in multithreaded way. html_item.loadHtml() composition.exportAsPDF(output_path) return output_path
def show_results(self): """Helper function for slot activated when the process is done. .. versionchanged:: 3.4 - removed parameters. .. note:: If you update this function, please report your change to safe.gui.widgets.dock.show_results too. :returns: Provides a report for writing to the dock. :rtype: str """ qgis_exposure = self.impact_function.exposure.qgis_layer() qgis_hazard = self.impact_function.hazard.qgis_layer() qgis_aggregation = self.impact_function.aggregation.qgis_layer() safe_impact_layer = self.impact_function.impact qgis_impact_layer = read_impact_layer(safe_impact_layer) keywords = self.keyword_io.read_keywords(qgis_impact_layer) json_path = os.path.splitext(qgis_impact_layer.source())[0] + '.json' # write postprocessing report to keyword postprocessor_data = self.impact_function.postprocessor_manager.\ get_json_data(self.impact_function.aggregator.aoi_mode) post_processing_report = m.Message() if os.path.exists(json_path): with open(json_path) as json_file: impact_data = json.load(json_file, object_pairs_hook=OrderedDict) impact_data['post processing'] = postprocessor_data write_json(impact_data, json_path) else: post_processing_report = self.impact_function.\ postprocessor_manager.get_output( self.impact_function.aggregator.aoi_mode) keywords['postprocessing_report'] = post_processing_report.to_html( suppress_newlines=True) self.keyword_io.write_keywords(qgis_impact_layer, keywords) # Get tabular information from impact layer report = m.Message() report.add(LOGO_ELEMENT) report.add(m.Heading(self.tr('Analysis Results'), **INFO_STYLE)) # If JSON Impact Data Exist, use JSON json_path = qgis_impact_layer.source()[:-3] + 'json' LOGGER.debug('JSON Path %s' % json_path) if os.path.exists(json_path): impact_template = get_report_template(json_file=json_path) impact_report = impact_template.generate_message_report() report.add(impact_report) else: report.add( self.keyword_io.read_keywords(qgis_impact_layer, 'impact_summary')) # append postprocessing report report.add(post_processing_report.to_html()) # Layer attribution comes last report.add(impact_attribution(keywords).to_html(True)) # Get requested style for impact layer of either kind style = safe_impact_layer.get_style_info() style_type = safe_impact_layer.get_style_type() # Determine styling for QGIS layer if safe_impact_layer.is_vector: if not style: # Set default style if possible pass elif style_type == 'categorizedSymbol': LOGGER.debug('use categorized') set_vector_categorized_style(qgis_impact_layer, style) elif style_type == 'graduatedSymbol': LOGGER.debug('use graduated') set_vector_graduated_style(qgis_impact_layer, style) elif safe_impact_layer.is_raster: if not style: qgis_impact_layer.setDrawingStyle("SingleBandPseudoColor") else: setRasterStyle(qgis_impact_layer, style) else: message = self.tr( 'Impact layer %s was neither a raster or a vector layer') % ( qgis_impact_layer.source()) # noinspection PyExceptionInherit raise ReadLayerError(message) legend = self.iface.legendInterface() # Insert the aggregation output above the input aggregation layer if self.show_intermediate_layers: add_above_layer(self.impact_function.aggregator.layer, qgis_aggregation) legend.setLayerVisible(self.impact_function.aggregator.layer, True) if self.hide_exposure_flag: # Insert the impact always above the hazard add_above_layer(qgis_impact_layer, qgis_hazard) else: # Insert the impact above the hazard and the exposure if # we don't hide the exposure. See #2899 add_above_layer(qgis_impact_layer, qgis_exposure, qgis_hazard) # In QGIS 2.14.2 and GDAL 1.11.3, if the exposure is in 3857, # the impact layer is in 54004, we need to change it. See issue #2790. if qgis_exposure.crs().authid() == 'EPSG:3857': if qgis_impact_layer.crs().authid() != 'EPSG:3857': epsg_3857 = QgsCoordinateReferenceSystem(3857) qgis_impact_layer.setCrs(epsg_3857) # make sure it is active in the legend - needed since QGIS 2.4 self.iface.setActiveLayer(qgis_impact_layer) # then zoom to it if self.zoom_to_impact_flag: self.iface.zoomToActiveLayer() if self.hide_exposure_flag: exposure_layer = self.get_exposure_layer() legend.setLayerVisible(exposure_layer, False) # Make the layer visible. Might be hidden by default. See #2925 legend.setLayerVisible(qgis_impact_layer, True) # Return text to display in report panel return report