diff --git a/u4py/io/tex_report.py b/u4py/io/tex_report.py index 205bb9b4ccccf2eda99950f2e0083223622e3575..0d9bb087af9fa0c35cbcaaae2b8af6f4ede5c197 100644 --- a/u4py/io/tex_report.py +++ b/u4py/io/tex_report.py @@ -58,22 +58,22 @@ def multi_report(output_path: os.PathLike): tex_file.write(tex) report_out_path = os.path.split(output_path)[0] - subprocess.run( - ["pdflatex", "-draftmode", f"{report_path}"], - cwd=report_out_path, - ) - subprocess.run( - ["pdflatex", "-draftmode", f"{report_path}"], - cwd=report_out_path, - ) - subprocess.run( - ["pdflatex", f"{report_path}"], - cwd=report_out_path, - ) - clean_aux_files(report_out_path) + run_tool_chain(report_path, report_out_path) -def main_report(output_path: os.PathLike): +def main_report( + output_path: os.PathLike, + title: str, + subtitle: str, + suffix: str = "", +): + """Generates a single report from all files in the `tex_includes` folder. + + :param output_path: The path to the output folder, where also the `tex_includes` are located. + :type output_path: os.PathLike + :param suffix: The suffix added to the report to distinguish different datasets, defaults to "" + :type suffix: str, optional + """ tex_folder = os.path.join(output_path, "tex_includes") include_list = [ os.path.join(tex_folder, fp) @@ -97,8 +97,8 @@ def main_report(output_path: os.PathLike): + "\\addtolength{\\cftsecnumwidth}{10pt}" # + "\\usepackage[margin=1in]{geometry}\n" + "\\begin{document}\n" - + "\\title{Detektierte Anomalien}\n" - + "\\subtitle{Atlas anomaler Bodenbewegungen in Hessen}\n" + + f"\\title{{{title}}}\n" + + f"\\subtitle{{{subtitle}}}\n" + "\\author{automatisch generierter Report aus U4Py}\n" + "\\date{\\today}\n" + "\\addTitleBox{Institut für Angewandte Geowissenschaften}\n\n" @@ -111,11 +111,27 @@ def main_report(output_path: os.PathLike): tex += f"\\input{{{incl}}}\n\\clearpage\n" tex += "\\end{document}" - report_path = os.path.join(output_path, "Site_Report.tex") + report_path = os.path.join(output_path, f"Site_Report{suffix}.tex") with open(report_path, "wt", encoding="utf-8", newline="\n") as tex_file: tex_file.write(tex) report_out_path = os.path.split(output_path)[0] + run_tool_chain(report_path, report_out_path, suffix) + + +def run_tool_chain( + report_path: os.PathLike, report_out_path: os.PathLike, suffix: str = "" +): + """Runs a 3x Latex toolchain, first two in draft mode and then full compilation. + + :param report_path: The path to the main file + :type report_path: os.PathLike + :param report_out_path: The output path of the final file + :type report_out_path: os.PathLike + :param suffix: A suffix that was added to the report_path, defaults to "" + :type suffix: str, optional + """ + clean_aux_files(report_out_path, suffix) subprocess.run( ["pdflatex", "-draftmode", f"{report_path}"], cwd=report_out_path, @@ -128,16 +144,17 @@ def main_report(output_path: os.PathLike): ["pdflatex", f"{report_path}"], cwd=report_out_path, ) - clean_aux_files(report_out_path) + clean_aux_files(report_out_path, suffix) -def clean_aux_files(report_out_path: os.PathLike): +def clean_aux_files(report_out_path: os.PathLike, suffix: str): tex_temps = [ "pdfa.xmpi", - "Site_Report.aux", - "Site_Report.log", - "Site_Report.out", - "Site_Report.xmpdata", + f"Site_Report{suffix}.aux", + f"Site_Report{suffix}.log", + f"Site_Report{suffix}.out", + f"Site_Report{suffix}.xmpdata", + f"Site_Report{suffix}.toc", ] for tp in tex_temps: fp = os.path.join(report_out_path, tp) @@ -170,13 +187,8 @@ def site_report( img_path = os.path.join(output_path, "known_features", f"{group:05}") # Create TeX code - - tex = ( - "\\section{" - + ",".join(row[1].locations.split(",")[:-4]) - + f" ({group})" - + "}\n\n" - ) + heading = sanitize_text(",".join(row[1].locations.split(",")[:-4])) + tex = "\\section{" + heading + f" ({group})" + "}\n\n" # Overview plot and satellite image tex += location(row[1]) @@ -244,23 +256,23 @@ def location(series: gp.GeoSeries) -> str: ).to_crs("EPSG:4326") lat = np.round(float(wgs_point.geometry.y.iloc[0]), 6) lng = np.round(float(wgs_point.geometry.x.iloc[0]), 6) - + address = sanitize_text(series.locations) tex = ( "\\subsection*{{Lokalität:}}\n" + "\\textbf{Adresse:} " - + f"{series.locations}\n\n" + + f"{address}\n\n" + f"\\textbf{{Koordinaten (UTM 32N):}} " + f"{int(series.geometry.centroid.y)}\\,N " + f"{int(series.geometry.centroid.x)}\\,E\n\n" + f"\\textbf{{Google Maps:}} " + f"\\href{{https://www.google.com/maps/place/{lat},{lng}/@{lat},{lng}/data=!3m1!1e3}}" - + f"{{\\faExternalLink {np.round(lat,3)}\\,N, {np.round(lng,3)}\\,E}}\n\n" + + f"{{\\faExternalLink {np.round(lat, 3)}\\,N, {np.round(lng, 3)}\\,E}}\n\n" + f"\\textbf{{Bing Maps:}} " + f"\\href{{https://bing.com/maps/default.aspx?cp={lat}~{lng}&style=h&lvl=15}}" - + f"{{\\faExternalLink {np.round(lat,3)}\\,N, {np.round(lng,3)}\\,E}}\n\n" + + f"{{\\faExternalLink {np.round(lat, 3)}\\,N, {np.round(lng, 3)}\\,E}}\n\n" + f"\\textbf{{OpenStreetMap:}} " + f"\\href{{http://www.openstreetmap.org/?lat={lat}&lon={lng}&zoom=17&layers=M}}" - + f"{{\\faExternalLink {np.round(lat,3)}\\,N, {np.round(lng,3)}\\,E}}\n\n" + + f"{{\\faExternalLink {np.round(lat, 3)}\\,N, {np.round(lng, 3)}\\,E}}\n\n" ) return tex @@ -327,6 +339,7 @@ def manual_description(series: gp.GeoSeries) -> str: series[f"manual_class_{ii}"] for ii in range(1, 4) if series[f"manual_class_{ii}"] + and series[f"manual_class_{ii}"] != "(empty)" ] ) prob_txt = ["wahrscheinlich", "möglicherweise"] @@ -386,11 +399,11 @@ def details_and_satellite(img_path: os.PathLike) -> str: "\\begin{figure}[h!]\n" + " \\centering\n" + " \\begin{subfigure}[][][t]{.49\\textwidth}\n" - + f" \\includegraphics[width=\\textwidth]{{{img_path+'_map.pdf'}}}\n" + + f" \\includegraphics[width=\\textwidth]{{{img_path + '_map.pdf'}}}\n" + " \\caption{Übersicht über das Gebiet der Gruppe inklusive verschiedener Geogefahren und der detektierten Anomalien (Kartengrundlage OpenStreetMap).}\n" + " \\end{subfigure}\n\hfill\n" + " \\begin{subfigure}[][][t]{.49\\textwidth}\n" - + f" \\includegraphics[width=\\textwidth]{{{img_path+'_satimg.pdf'}}}\n" + + f" \\includegraphics[width=\\textwidth]{{{img_path + '_satimg.pdf'}}}\n" + " \\caption{Luftbild basierend auf ESRI Imagery.}\n" + " \\end{subfigure}\n" + " \\caption{Lokalität der Anomalie.}" @@ -448,7 +461,7 @@ def difference(img_path: os.PathLike) -> str: tex += ( "\\begin{figure}[!ht]\n" + " \\centering" - + f" \\includegraphics[width=.9\\textwidth]{{{img_path+'_diffplan.pdf'}}}\n" + + f" \\includegraphics[width=.9\\textwidth]{{{img_path + '_diffplan.pdf'}}}\n" + " \\caption{Differenzenplan im Gebiet.}\n" + "\\end{figure}\n" ) @@ -457,7 +470,7 @@ def difference(img_path: os.PathLike) -> str: tex += ( "\\begin{figure}[!ht]\n" + " \\centering" - + f" \\includegraphics[width=.9\\textwidth]{{{img_path+'_dem.pdf'}}}\n" + + f" \\includegraphics[width=.9\\textwidth]{{{img_path + '_dem.pdf'}}}\n" + " \\caption{Digitales Höhenmodell (Schummerung).}\n" + "\\end{figure}\n\n" ) @@ -529,12 +542,12 @@ def topography(series: gp.GeoSeries, img_path: os.PathLike) -> str: tex += ( "\n\\begin{figure}[!ht]\n" + " \\begin{subfigure}[][][t]{.49\\textwidth}\n" - + f" \\includegraphics[width=\\textwidth]{{{img_path+'_slope.pdf'}}}\n" + + f" \\includegraphics[width=\\textwidth]{{{img_path + '_slope.pdf'}}}\n" + " \\caption{Steigung}\n" + " \\end{subfigure}\n\hfill\n" + " \\begin{subfigure}[][][t]{.49\\textwidth}\n" + "\\centering\n" - + f" \\includegraphics[width=\\textwidth]{{{img_path+'_aspect.pdf'}}}\n" + + f" \\includegraphics[width=\\textwidth]{{{img_path + '_aspect.pdf'}}}\n" + " \\caption{Exposition.}\n" + " \\end{subfigure}\n\hfill\n" + " \\caption{Topographie im Gebiet.}" @@ -545,7 +558,7 @@ def topography(series: gp.GeoSeries, img_path: os.PathLike) -> str: tex += ( "\\begin{figure}[!ht]\n" + " \\centering\n" - + f" \\includegraphics[width=.95\\textwidth]{{{img_path+'_aspect_slope.pdf'}}}\n" + + f" \\includegraphics[width=.95\\textwidth]{{{img_path + '_aspect_slope.pdf'}}}\n" + " \\caption{Steigung und Exposition}\n" + "\\end{figure}\n" ) @@ -698,7 +711,7 @@ def psi_map(img_path: os.PathLike) -> str: "\\subsection*{InSAR Daten}\n\n" + "\\begin{figure}[h!]\n" + " \\centering\n" - + f" \\includegraphics[width=\\textwidth]{{{img_path+'_psi.png'}}}\n" + + f" \\includegraphics[width=\\textwidth]{{{img_path + '_psi.png'}}}\n" + " \\caption{Persistent scatterer und Zeitreihe der Deformation " + "im Gebiet der Gruppe.}\n" + "\\end{figure}\n\n" @@ -827,12 +840,12 @@ def geology(img_path) -> str: "\n\\subsection*{Geologie}\n\n" + "\\begin{figure}[H]\n" + "\\centering\n" - + f" \\includegraphics[width=\\textwidth]{{{img_path+'_GK25.pdf'}}}\n" + + f" \\includegraphics[width=\\textwidth]{{{img_path + '_GK25.pdf'}}}\n" + "\\end{figure}\n" + "\\vspace{-2ex}\n" + "\\begin{figure}[H]\n" + "\\centering\n" - + f" \\includegraphics[width=.75\\textwidth]{{{img_path+'_GK25_leg.pdf'}}}\n" + + f" \\includegraphics[width=.75\\textwidth]{{{img_path + '_GK25_leg.pdf'}}}\n" + " \\caption{Geologie im Gebiet basierend auf GK25 (Quelle: HLNUG).}\n" + "\\end{figure}\n\n" ) @@ -844,12 +857,12 @@ def hydrogeology(img_path: os.PathLike) -> str: "\n\\subsection*{Hydrogeologie}\n\n" + "\\begin{figure}[H]\n" + "\\centering\n" - + f" \\includegraphics[width=\\textwidth]{{{img_path+'_HUEK200.pdf'}}}\n" + + f" \\includegraphics[width=\\textwidth]{{{img_path + '_HUEK200.pdf'}}}\n" + "\\end{figure}\n" + "\\vspace{-2ex}\n" + "\\begin{figure}[H]\n" + "\\centering\n" - + f" \\includegraphics[width=.75\\textwidth]{{{img_path+'_HUEK200_leg.pdf'}}}\n" + + f" \\includegraphics[width=.75\\textwidth]{{{img_path + '_HUEK200_leg.pdf'}}}\n" + " \\caption{Hydrogeologische Einheiten im Gebiet basierend auf HÜK200 (Quelle: HLNUG).}\n" + "\\end{figure}\n\n" ) @@ -861,12 +874,12 @@ def soils(img_path: os.PathLike) -> str: "\n\\subsection*{Bodengruppen}\n\n" + "\\begin{figure}[H]\n" + "\\centering\n" - + f" \\includegraphics[width=\\textwidth]{{{img_path+'_BFD50.pdf'}}}\n" + + f" \\includegraphics[width=\\textwidth]{{{img_path + '_BFD50.pdf'}}}\n" + "\\end{figure}\n" + "\\vspace{-2ex}\n" + "\\begin{figure}[H]\n" + "\\centering\n" - + f" \\includegraphics[width=.75\\textwidth]{{{img_path+'_BFD50_leg.pdf'}}}\n" + + f" \\includegraphics[width=.75\\textwidth]{{{img_path + '_BFD50_leg.pdf'}}}\n" + " \\caption{Bodenhauptgruppen im Gebiet basierend auf der BFD50 (Quelle: HLNUG).}\n" + "\\end{figure}\n\n" ) @@ -1086,3 +1099,18 @@ def hlnug_description(hld: gp.GeoDataFrame) -> str: tex = tex.replace("_", " ") return tex + + +def sanitize_text(in_str: str) -> str: + """Escapes all special characters in the input string for LaTeX. + + :param in_str: The input string, possibly containing symbols with special meaning in LaTeX. + :type in_str: str + :return: The sanitized string. + :rtype: str + """ + # Somehow regex did not work properly... + characters = ["&", "%", "$", "#", "_", "{", "}"] + for char in characters: + in_str = in_str.replace(char, f"\\{char}") + return in_str diff --git a/u4py/plotting/axes.py b/u4py/plotting/axes.py index f58bebaaeb2484f24b18f5248cd7f092c652d85b..c62fc8c53b99c817d2a758b8997c90909ecf070e 100644 --- a/u4py/plotting/axes.py +++ b/u4py/plotting/axes.py @@ -508,7 +508,7 @@ def add_basemap( logging.info( "Connection to Server timed out. No Basemap Added." ) - except urlexept.TimeoutError: + except: logging.info("Connection to Server timed out. No Basemap Added.") OSM_AVAILABLE = False diff --git a/u4py/plotting/plots.py b/u4py/plotting/plots.py index 818e4d326e1f9565d74e913c88ac8e53c9d71c00..cade3a1e3e072f96631888f081c8b8fdb49d4248 100644 --- a/u4py/plotting/plots.py +++ b/u4py/plotting/plots.py @@ -6,6 +6,7 @@ from __future__ import annotations import logging import os +import warnings from typing import Iterable, Tuple import contextily @@ -26,7 +27,7 @@ import u4py.io.tiff as u4tiff import u4py.plotting.axes as u4ax import u4py.plotting.formatting as u4plotfmt -GLOBAL_DPI = 150 +GLOBAL_DPI = 72 def plot_inversion_results( @@ -497,7 +498,8 @@ def plot_shape( ax.annotate(slope_str, (0.025, 0.025), xycoords="axes fraction", zorder=4) fig.tight_layout() u4ax.add_basemap( - ax=ax, crs=sub_set.crs, source=contextily.providers.CartoDB.Positron + ax=ax, + crs=sub_set.crs, # source=contextily.providers.CartoDB.Positron ) fig.savefig(os.path.join(save_folder, f"Site_{group}.png")) plt.close(fig) @@ -665,11 +667,11 @@ def geology_map( # Formatting and other stuff u4plotfmt.add_scalebar(ax=ax, width=plot_buffer * 4) - u4ax.add_basemap( - ax=ax, - crs=geology_data.crs, - source=contextily.providers.CartoDB.Positron, - ) + # u4ax.add_basemap( + # ax=ax, + # crs=geology_data.crs, + # # source=contextily.providers.CartoDB.Positron, + # ) fig.savefig(os.path.join(output_path, f"{row[1].group:05}_GK25.png")) fig.savefig(os.path.join(output_path, f"{row[1].group:05}_GK25.pdf")) @@ -786,11 +788,11 @@ def hydrogeology_map( # Formatting and other stuff u4plotfmt.add_scalebar(ax=ax, width=plot_buffer * 4) - u4ax.add_basemap( - ax=ax, - crs=hydro_units_data.crs, - source=contextily.providers.CartoDB.Positron, - ) + # u4ax.add_basemap( + # ax=ax, + # crs=hydro_units_data.crs, + # # source=contextily.providers.CartoDB.Positron, + # ) fig.savefig( os.path.join(output_path, f"{row[1].group:05}_HUEK200.png") ) @@ -916,11 +918,11 @@ def topsoil_map( # Formatting and other stuff u4plotfmt.add_scalebar(ax=ax, width=plot_buffer * 4) - u4ax.add_basemap( - ax=ax, - crs=soil_data.crs, - source=contextily.providers.CartoDB.Positron, - ) + # u4ax.add_basemap( + # ax=ax, + # crs=soil_data.crs, + # # source=contextily.providers.CartoDB.Positron, + # ) fig.savefig(os.path.join(output_path, f"{row[1].group:05}_BFD50.png")) fig.savefig(os.path.join(output_path, f"{row[1].group:05}_BFD50.pdf")) @@ -1468,8 +1470,10 @@ def detailed_map( verticalalignment="bottom", zorder=10, ) - h, l = ax.get_legend_handles_labels() - rdbu = mcm.get_cmap("RdBu") + with warnings.catch_warnings(): + # Catches UserWarning for unsupported handles. + warnings.simplefilter("ignore") + h, _ = ax.get_legend_handles_labels() h.append(mpatches.Patch(ec=mcm.RdBu(0), fc="None", label="Senkung")) h.append(mpatches.Patch(ec=mcm.RdBu(256), fc="None", label="Hebung")) ax.legend( diff --git a/u4py/scripts/gis_workflows/Classify_Shapes.py b/u4py/scripts/gis_workflows/Classify_Shapes.py index c0ef44ae7c5266bba0f631ba8275c6647661bde2..9f2de68e95363dd79263b397d1875dcf2713004a 100644 --- a/u4py/scripts/gis_workflows/Classify_Shapes.py +++ b/u4py/scripts/gis_workflows/Classify_Shapes.py @@ -52,7 +52,7 @@ def main(): # sub_region = create_test_region() # shp_gdf = u4gpkg.load_gpkg_data_region_ogr(sub_region, shp_file) shp_gdf = gp.read_file(shp_file).to_crs("EPSG:32632") - shp_gdf = shp_gdf[:100] + # shp_gdf = shp_gdf[:100] if "groups" in shp_gdf.keys(): unique_groups = np.unique(shp_gdf.groups) else: diff --git a/u4py/scripts/gis_workflows/PostProcess_ClassifiedShapes.py b/u4py/scripts/gis_workflows/PostProcess_ClassifiedShapes.py index 3320f65a6d8ecb6d1fd7330157067a380e9eb856..757a23f693a81a2e3b548c74662d73010f63eb17 100644 --- a/u4py/scripts/gis_workflows/PostProcess_ClassifiedShapes.py +++ b/u4py/scripts/gis_workflows/PostProcess_ClassifiedShapes.py @@ -7,7 +7,6 @@ import configparser import datetime import logging import os -import warnings from pathlib import Path import geopandas as gp @@ -24,7 +23,7 @@ import u4py.utils.projects as u4proj def main(): project = u4proj.get_project( proj_path=Path( - "~/Documents/umwelt4/PostProcess_ClassifiedShapesHLNUG.u4project" + "~/Documents/umwelt4/PostProcess_ClassifiedShapes.u4project" ).expanduser(), required=[ "base_path", @@ -36,24 +35,35 @@ def main(): interactive=False, ) overwrite = False - use_filtered = False - use_parallel = True + use_filtered = True + use_parallel = False generate_plots = True - overwrite_plots = True + overwrite_plots = False generate_pdf = True single_report = True - is_hlnug = True + is_hlnug = False + + # Setting up names of report + # report_title = "Große Bewegungsanomalien in Hessen" + # report_subtitle = ( + # "Anomalien mit mindestens 20000\,m\\textsuperscript{3} Volumenänderung" + # ) + # report_suffix = "_onlyLarge" + report_title = "Bewegungsanomalien in Hessen" + report_subtitle = "Anomalien in der Nähe bekannter Geogefahren" + report_suffix = "_hazard" # Setting up paths output_path = os.path.join( - project["paths"]["output_path"], "Detailed_Maps" + project["paths"]["output_path"], f"Detailed_Maps{report_suffix}" ) os.makedirs(output_path, exist_ok=True) class_shp_fp = os.path.join( project["paths"]["results_path"], "Classified_Shapes.gpkg" ) cls_shp_fp_filtered = os.path.join( - project["paths"]["results_path"], "Filtered_Classified_Shapes.gpkg" + project["paths"]["results_path"], + f"Filtered_Classified_Shapes{report_suffix}.gpkg", ) hlnug_path = os.path.join( project["paths"]["places_path"], @@ -76,17 +86,13 @@ def main(): gdf_filtered = filter_shapes( class_shp_fp, cls_shp_fp_filtered, project ) - gdf_filtered = reverse_geolocate( - gdf_filtered, project, cls_shp_fp_filtered - ) + gdf_filtered = reverse_geolocate(gdf_filtered, cls_shp_fp_filtered) else: gdf_filtered = gp.read_file(cls_shp_fp_filtered) else: if not os.path.exists(cls_shp_fp_filtered) or overwrite: gdf_filtered = gp.read_file(class_shp_fp) - gdf_filtered = reverse_geolocate( - gdf_filtered, project, cls_shp_fp_filtered - ) + gdf_filtered = reverse_geolocate(gdf_filtered, cls_shp_fp_filtered) else: gdf_filtered = gp.read_file(cls_shp_fp_filtered) @@ -139,7 +145,9 @@ def main(): row, output_path, "tex_includes", hlnug_data=hlnug_data ) if single_report: - u4rep.main_report(output_path) + u4rep.main_report( + output_path, report_title, report_subtitle, report_suffix + ) else: u4rep.multi_report(output_path) @@ -170,13 +178,10 @@ def filter_shapes( def reverse_geolocate( gdf_filtered: gp.GeoDataFrame, - project: configparser.ConfigParser, output_path: os.PathLike, ) -> gp.GeoDataFrame: # Do a reverse geocoding to get the address of the locations. - cached_locations = os.path.join( - project["paths"]["results_path"], "cached_locations.txt" - ) + cached_locations = os.path.join(output_path, "cached_locations.txt") if not os.path.exists(cached_locations): locations = u4spatial.reverse_geolocate(gdf_filtered) with open(cached_locations, "wt", encoding="utf-8") as cache: @@ -266,36 +271,36 @@ def map_worker( plot_buffer=100, overwrite=overwrite, ) - # u4plots.dem_map( - # row, - # crs, - # output_path, - # "known_features", - # dem_path, - # contour_path, - # plot_buffer=100, - # overwrite=overwrite, - # ) - # u4plots.slope_map( - # row, - # crs, - # output_path, - # "known_features", - # dem_path, - # contour_path, - # plot_buffer=100, - # overwrite=overwrite, - # ) - # u4plots.aspect_map( - # row, - # crs, - # output_path, - # "known_features", - # dem_path, - # contour_path, - # plot_buffer=100, - # overwrite=overwrite, - # ) + u4plots.dem_map( + row, + crs, + output_path, + "known_features", + dem_path, + contour_path, + plot_buffer=100, + overwrite=overwrite, + ) + u4plots.slope_map( + row, + crs, + output_path, + "known_features", + dem_path, + contour_path, + plot_buffer=100, + overwrite=overwrite, + ) + u4plots.aspect_map( + row, + crs, + output_path, + "known_features", + dem_path, + contour_path, + plot_buffer=100, + overwrite=overwrite, + ) u4plots.aspect_slope_map( row, crs,