From 5099374b7b877beb533e1123f0b10cdc5b8a6cbc Mon Sep 17 00:00:00 2001 From: kauevestena Date: Tue, 17 Sep 2024 17:39:55 -0300 Subject: [PATCH] more symbols, some black styling --- assets/map_symbols/footway_categories.png | Bin 0 -> 16412 bytes assets/map_symbols/lit.png | Bin 6732 -> 6713 bytes assets/map_symbols/smoothness.png | Bin 11941 -> 12284 bytes assets/map_symbols/surface.png | Bin 26552 -> 26745 bytes assets/map_symbols/tactile_paving.png | Bin 4741 -> 4695 bytes assets/map_symbols/traffic_calming.png | Bin 3806 -> 3860 bytes assets/map_symbols/wheelchair.png | Bin 6027 -> 6061 bytes constants.py | 810 ++++++++++------------ functions.py | 448 +++++++----- webmap/create_webmap_new.py | 50 +- webmap/standalone_legend.py | 47 +- webmap/webmap_lib.py | 437 ++++++------ 12 files changed, 944 insertions(+), 848 deletions(-) create mode 100644 assets/map_symbols/footway_categories.png diff --git a/assets/map_symbols/footway_categories.png b/assets/map_symbols/footway_categories.png new file mode 100644 index 0000000000000000000000000000000000000000..30db5f722912776a84d5d4bcf2f48131b1d8d1e1 GIT binary patch literal 16412 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 literal 0 HcmV?d00001 diff --git a/assets/map_symbols/lit.png b/assets/map_symbols/lit.png index 5fde8ba07694deae45b2dcb51f3e6f159ce88211..fa82d4105ad866f1a3ce6380c71bfb85fa0d2371 100644 GIT binary patch literal 6713 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 bm-r9R5VgSEqvzn09Y{_}NwQSjAmG0Md#Uf} diff --git a/assets/map_symbols/smoothness.png b/assets/map_symbols/smoothness.png index 52e6652b689d1941cd38a2840d6ad7283521b728..453b467986fb7ad78e62a8b4846fc59d914f4bda 100644 GIT binary patch literal 12284 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 SJ~-ro?%mN=eXnBv;=cfvyYHg_ literal 4741 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 diff --git a/assets/map_symbols/wheelchair.png b/assets/map_symbols/wheelchair.png index 538655430b3a9547cf1bca1ed7e88218f577d094..e0a1ec48fbc23ac7e9d887dae47319d48a96b5c3 100644 GIT binary patch literal 6061 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 literal 6027 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 diff --git a/constants.py b/constants.py index d1ff7e6..811668a 100644 --- a/constants.py +++ b/constants.py @@ -1,5 +1,6 @@ import sys, os -sys.path.append('.') + +sys.path.append(".") from config import * @@ -13,7 +14,7 @@ # global max zoom level max_zoom = 22 -data_format = '.parquet' +data_format = ".parquet" # node archives general paths map_page_name = "./map.html" @@ -22,123 +23,134 @@ boundaries_path = "./data/boundaries" + data_format boundaries_geojson_path = "./data/boundaries.geojson" boundaries_md_path = "./data/boundaries_md.json" -workflows_path = '.github/workflows' +workflows_path = ".github/workflows" # data folderpaths: -improper_geoms_folderpath = 'data/improper_geoms' -disjointed_folderpath = 'data/disjointed' -versioning_folderpath = 'data/versioning' -other_footways_folderpath = 'data/other_footways' -tiles_folderpath = 'data/tiles' -vrts_folderpath = 'data/vrts' +improper_geoms_folderpath = "data/improper_geoms" +disjointed_folderpath = "data/disjointed" +versioning_folderpath = "data/versioning" +other_footways_folderpath = "data/other_footways" +tiles_folderpath = "data/tiles" +vrts_folderpath = "data/vrts" other_footways_subcatecories = { - 'stairways' : {'highway':['steps']}, - 'main_footways' : {'highway':['footway','living_street'],'foot':['designated'],'footway': ['alley','path','yes']}, - 'potential_footways' : {'highway':['path','track']}, - 'informal_footways' : {'foot':['yes','permissive']}, - 'pedestrian_areas' : {} #defined only by geometry type (Polygon,Multipolygon) + "stairways": {"highway": ["steps"]}, + "main_footways": { + "highway": ["footway", "living_street"], + "foot": ["designated"], + "footway": ["alley", "path", "yes"], + }, + "potential_footways": {"highway": ["path", "track"]}, + "informal_footways": {"foot": ["yes", "permissive"]}, + "pedestrian_areas": {}, # defined only by geometry type (Polygon,Multipolygon) } # establishing other footways geometry types, default is 'LineString' -other_footways_geometry_types = {k:'LineString' for k, v in other_footways_subcatecories.items()} -other_footways_geometry_types['pedestrian_areas'] = 'Polygon' +other_footways_geometry_types = { + k: "LineString" for k, v in other_footways_subcatecories.items() +} +other_footways_geometry_types["pedestrian_areas"] = "Polygon" data_layer_descriptions = { - 'kerbs' : 'Access points in the kerb lane where the sidewalk and the road meet, along a crossing.', - 'sidewalks' : 'A footway that is juxtaposed to a road, a type of sidepath.', - 'crossings' : 'The line that allows pedestrians to cross some road.', - 'other_footways' : { - 'stairways' : 'Pathways composed of steps.', - 'main_footways' : 'Pathways which main usage is pedestrian displacement.', - 'potential_footways' : 'Pathways with vague description, generally usable for pedestrians, but sometimes not as its main or sole purpose, such as some rural tracks.', - 'informal_footways' : 'Pathways that are not made for pedestrian usage, but they generally used due to the absence of proper footways.', - 'pedestrian_areas' : 'Areas where pedestrians can generally displace freely in normal circumstances.' - } + "kerbs": "Access points in the kerb lane where the sidewalk and the road meet, along a crossing.", + "sidewalks": "A footway that is juxtaposed to a road, a type of sidepath.", + "crossings": "The line that allows pedestrians to cross some road.", + "other_footways": { + "stairways": "Pathways composed of steps.", + "main_footways": "Pathways which main usage is pedestrian displacement.", + "potential_footways": "Pathways with vague description, generally usable for pedestrians, but sometimes not as its main or sole purpose, such as some rural tracks.", + "informal_footways": "Pathways that are not made for pedestrian usage, but they generally used due to the absence of proper footways.", + "pedestrian_areas": "Areas where pedestrians can generally displace freely in normal circumstances.", + }, } # ogr2ogr path -OGR2OGR_PATH = 'ogr2ogr' +OGR2OGR_PATH = "ogr2ogr" layer_tags_dict = { - 'kerbs': {'kerb': ['lowered','raised','flush','rolled','no','yes'], 'barrier': ['kerb']}, - 'sidewalks': {'footway': ['sidewalk']}, - 'crossings': {'footway': ['crossing']}, - 'other_footways' : OTHER_FOOTWAY_RULES - } + "kerbs": { + "kerb": ["lowered", "raised", "flush", "rolled", "no", "yes"], + "barrier": ["kerb"], + }, + "sidewalks": {"footway": ["sidewalk"]}, + "crossings": {"footway": ["crossing"]}, + "other_footways": OTHER_FOOTWAY_RULES, +} layer_exclusion_tags = { - 'kerbs': {}, - 'sidewalks': {}, - 'crossings': {}, - 'other_footways' : OTHER_FOOTWAY_EXCLUSION_RULES, + "kerbs": {}, + "sidewalks": {}, + "crossings": {}, + "other_footways": OTHER_FOOTWAY_EXCLUSION_RULES, } bbox_as_list = () # data paths -sidewalks_path = 'data/sidewalks' + data_format -crossings_path = 'data/crossings' + data_format -kerbs_path = 'data/kerbs' + data_format -other_footways_path = 'data/other_footways' + data_format +sidewalks_path = "data/sidewalks" + data_format +crossings_path = "data/crossings" + data_format +kerbs_path = "data/kerbs" + data_format +other_footways_path = "data/other_footways" + data_format -sidewalks_path_raw = 'data/sidewalks_raw' + data_format -crossings_path_raw = 'data/crossings_raw' + data_format -kerbs_path_raw = 'data/kerbs_raw' + data_format -other_footways_path_raw = 'data/other_footways_raw' + data_format +sidewalks_path_raw = "data/sidewalks_raw" + data_format +crossings_path_raw = "data/crossings_raw" + data_format +kerbs_path_raw = "data/kerbs_raw" + data_format +other_footways_path_raw = "data/other_footways_raw" + data_format -sidewalks_path_versioning = 'data/versioning/sidewalks_versioning.json' -crossings_path_versioning = 'data/versioning/crossings_versioning.json' -kerbs_path_versioning = 'data/versioning/kerbs_versioning.json' -other_footways_path_versioning = 'data/versioning/other_footways_versioning.json' +sidewalks_path_versioning = "data/versioning/sidewalks_versioning.json" +crossings_path_versioning = "data/versioning/crossings_versioning.json" +kerbs_path_versioning = "data/versioning/kerbs_versioning.json" +other_footways_path_versioning = "data/versioning/other_footways_versioning.json" # data quality jsons path -feat_keys_path = 'quality_check/feature_keys.json' -keys_without_wiki_path = 'quality_check/keys_without_wiki.json' -unique_values_path = 'quality_check/unique_tag_values.json' -valid_values_path = 'quality_check/valid_tag_values.json' +feat_keys_path = "quality_check/feature_keys.json" +keys_without_wiki_path = "quality_check/keys_without_wiki.json" +unique_values_path = "quality_check/unique_tag_values.json" +valid_values_path = "quality_check/valid_tag_values.json" # node homepage: -user_basepage_url = f'https://{USERNAME}.github.io/' -node_homepage_url = f'https://{USERNAME}.github.io/{REPO_NAME}/' -data_folder_url = f'https://{USERNAME}.github.io/{REPO_NAME}/data/' -data_updating_url = f'https://{USERNAME}.github.io/{REPO_NAME}/data/data_updating.html' +user_basepage_url = f"https://{USERNAME}.github.io/" +node_homepage_url = f"https://{USERNAME}.github.io/{REPO_NAME}/" +data_folder_url = f"https://{USERNAME}.github.io/{REPO_NAME}/data/" +data_updating_url = f"https://{USERNAME}.github.io/{REPO_NAME}/data/data_updating.html" # codebase as page: -codebase_homepage = 'https://kauevestena.github.io/oswm_codebase/' +codebase_homepage = "https://kauevestena.github.io/oswm_codebase/" paths_dict = { - 'data' :{ - 'sidewalks': sidewalks_path, - 'crossings': crossings_path, - 'kerbs': kerbs_path, - 'other_footways' : other_footways_path + "data": { + "sidewalks": sidewalks_path, + "crossings": crossings_path, + "kerbs": kerbs_path, + "other_footways": other_footways_path, }, - 'data_raw' : { - 'sidewalks': sidewalks_path_raw, - 'crossings': crossings_path_raw, - 'kerbs': kerbs_path_raw, - 'other_footways' : other_footways_path_raw + "data_raw": { + "sidewalks": sidewalks_path_raw, + "crossings": crossings_path_raw, + "kerbs": kerbs_path_raw, + "other_footways": other_footways_path_raw, }, - 'versioning' : { - 'sidewalks': sidewalks_path_versioning, - 'crossings': crossings_path_versioning, - 'kerbs': kerbs_path_versioning, - 'other_footways' : other_footways_path_versioning + "versioning": { + "sidewalks": sidewalks_path_versioning, + "crossings": crossings_path_versioning, + "kerbs": kerbs_path_versioning, + "other_footways": other_footways_path_versioning, }, - 'other_footways_subcategories' : {}, - 'map_layers' : { - 'sidewalks': sidewalks_path, - 'crossings': crossings_path, - 'kerbs': kerbs_path, + "other_footways_subcategories": {}, + "map_layers": { + "sidewalks": sidewalks_path, + "crossings": crossings_path, + "kerbs": kerbs_path, }, } # paths for other_footways subcategories: for subcategory in other_footways_subcatecories: - subcategory_path = os.path.join(other_footways_folderpath, subcategory+data_format) - paths_dict['other_footways_subcategories'][subcategory] = subcategory_path - paths_dict['map_layers'][subcategory] = subcategory_path + subcategory_path = os.path.join( + other_footways_folderpath, subcategory + data_format + ) + paths_dict["other_footways_subcategories"][subcategory] = subcategory_path + paths_dict["map_layers"][subcategory] = subcategory_path # max radius to cut off unconnected crossings and kerbs @@ -148,498 +160,426 @@ default_score = 0.5 fields_values_properties = { - 'sidewalks':{ - 'surface': { + "sidewalks": { + "surface": { # colorscheme 12-class Set3 from colorbrewer (thx!!), avaliiable at: # https://colorbrewer2.org/?type=qualitative&scheme=Set3&n=12 - - 'asphalt':{ - 'score_default' : 100, - 'color' : '#fb8072', # + "asphalt": { + "score_default": 100, + "color": "#fb8072", # }, - 'concrete':{ - 'score_default' : 100, - 'color' : '#80b1d3', + "concrete": { + "score_default": 100, + "color": "#80b1d3", }, - 'concrete:plates':{ - 'score_default' : 70, - 'color' : '#fccde5', # + "concrete:plates": { + "score_default": 70, + "color": "#fccde5", # }, - 'paving_stones':{ - 'score_default' : 90, - 'color' : '#bebada', # + "paving_stones": { + "score_default": 90, + "color": "#bebada", # }, - 'sett':{ - 'score_default' : 60, - 'color' : '#ffed6f', # + "sett": { + "score_default": 60, + "color": "#ffed6f", # }, - - 'cobblestone':{ - 'score_default' : 60, - 'color' : '#ffed6f', # + "cobblestone": { + "score_default": 60, + "color": "#ffed6f", # }, - - 'unhewn_cobblestone':{ - 'score_default' : 50, - 'color' : '#ffffb3', #black + "unhewn_cobblestone": { + "score_default": 50, + "color": "#ffffb3", # black }, - - 'ground':{ - 'score_default' : 30, - 'color' : '#fdb462' }, # - 'dirt':{ - 'score_default' : 30, - 'color' : '#fdb462' }, # - 'earth':{ - 'score_default' : 30, - 'color' : '#fdb462', # + "ground": {"score_default": 30, "color": "#fdb462"}, # + "dirt": {"score_default": 30, "color": "#fdb462"}, # + "earth": { + "score_default": 30, + "color": "#fdb462", # }, - 'sand':{ - 'score_default' : 30, - 'color' : '#fdb462', # + "sand": { + "score_default": 30, + "color": "#fdb462", # }, - 'grass':{ - 'score_default' : 30, - 'color' : '#b3de69', # + "grass": { + "score_default": 30, + "color": "#b3de69", # }, # 'grass_paver':{ # 'score_default' : 3, # 'color' : '#000000', #black # }, - - 'paved':{ - 'score_default' : 60, # equals to worst paved: sett - 'color' : '#ffffff', # white + "paved": { + "score_default": 60, # equals to worst paved: sett + "color": "#ffffff", # white }, - 'unpaved':{ - 'score_default' : 30, - 'color' : '#d9d9d9', # + "unpaved": { + "score_default": 30, + "color": "#d9d9d9", # }, - # a sample for uncommon values: - - 'gravel':{ - 'score_default' : 30, - 'color' : '#bc80bd', # + "gravel": { + "score_default": 30, + "color": "#bc80bd", # }, - - 'compacted':{ - 'score_default' : 30, - 'color' : '#bc80bd', # + "compacted": { + "score_default": 30, + "color": "#bc80bd", # }, - - - 'ceramic:tiles':{ - 'score_default' : 70, - 'color' : '#bc80bd', # + "ceramic:tiles": { + "score_default": 70, + "color": "#bc80bd", # }, - - 'wood':{ - 'score_default' : 50, - 'color' : '#bc80bd', # + "wood": { + "score_default": 50, + "color": "#bc80bd", # }, - - 'metal':{ - 'score_default' : 100, - 'color' : '#bc80bd', # + "metal": { + "score_default": 100, + "color": "#bc80bd", # }, - # 'Petit_Pavê':{ # 'score_default' : 65, # 'color' : '#bc80bd', # # }, - # for the filled ones: - '?':{ - 'score_default' : 10, - 'color' : '#434343', # + "?": { + "score_default": 10, + "color": "#434343", # }, }, - - 'wheelchair': { - '?':{ - 'score_default' : 0, # equivalent to "very horrible" - 'color' : '#434343', # + "wheelchair": { + "yes": { + "score_default": 0, # equivalent to "very horrible" + "color": "#91bfdb", # }, - - 'no':{ - 'score_default' : 0, # equivalent to "very horrible" - 'color' : '#fc8d59', # + "designated": { + "score_default": 0, # equivalent to "very horrible" + "color": "#91bfdb", # }, - - - 'limited':{ - 'score_default' : 0, # equivalent to "very horrible" - 'color' : '#ffffbf', # + "limited": { + "score_default": 0, # equivalent to "very horrible" + "color": "#ffffbf", # }, - - 'yes':{ - 'score_default' : 0, # equivalent to "very horrible" - 'color' : '#91bfdb', # + "no": { + "score_default": 0, # equivalent to "very horrible" + "color": "#fc8d59", # }, - - 'designated':{ - 'score_default' : 0, # equivalent to "very horrible" - 'color' : '#91bfdb', # + "?": { + "score_default": 0, # equivalent to "very horrible" + "color": "#434343", # }, - - - - - }, - - - 'smoothness' : { - # for absence: - '?':{ - 'score_default' : 40, # equivalent to "very horrible" - 'color' : '#434343', # - }, - + "smoothness": { # color scheme: ColorBrewer (thx!!) 11-class RdYlBu - # valid: - 'excellent':{ - 'score_default' : 10, - 'color' : '#4575b4', # + "excellent": { + "score_default": 10, + "color": "#4575b4", # }, - 'good':{ - 'score_default' : 90, - 'color' : '#abd9e9', # + "good": { + "score_default": 90, + "color": "#abd9e9", # }, - 'intermediate':{ - 'score_default' : 70, - 'color' : '#ffffbf', # + "intermediate": { + "score_default": 70, + "color": "#ffffbf", # }, - 'bad':{ - 'score_default' : 50, - 'color' : '#fdae61', # + "bad": { + "score_default": 50, + "color": "#fdae61", # }, - 'very_bad':{ - 'score_default' : 40, - 'color' : '#fdae61', # + "very_bad": { + "score_default": 40, + "color": "#fdae61", # }, - 'horrible':{ - 'score_default' : 20, - 'color' : '#f46d43', # + "horrible": { + "score_default": 20, + "color": "#f46d43", # }, - 'very_horrible':{ - 'score_default' : 10, - 'color' : '#f46d43', # + "very_horrible": { + "score_default": 10, + "color": "#f46d43", # }, - 'impassable':{ - 'score_default' : 0, - 'color' : '#a50026', # + "impassable": { + "score_default": 0, + "color": "#a50026", # }, - - - # invalid values must be handled individually + # for absence: + "?": { + "score_default": 40, # equivalent to "very horrible" + "color": "#434343", # + }, + # invalid values must be handled individually }, - 'lit' : { - '?':{ - 'score_default' : 10, - 'color' : '#434343', # + "lit": { + "yes": { + "score_default": 10, + "color": "#ffff99", # }, - - 'yes':{ - 'score_default' : 10, - 'color' : '#ffff99', # + "automatic": { + "score_default": 10, + "color": "#ffff99", # }, - 'automatic':{ - 'score_default' : 10, - 'color' : '#ffff99', # + "24/7": { + "score_default": 10, + "color": "#ffff99", # }, - 'no':{ - 'score_default' : 10, - 'color' : '#6a3d9a', # + "no": { + "score_default": 10, + "color": "#6a3d9a", # }, - 'disused':{ - 'score_default' : 10, - 'color' : '#6a3d9a', # + "disused": { + "score_default": 10, + "color": "#6a3d9a", # }, - '24/7':{ - 'score_default' : 10, - 'color' : '#ffff99', # + "?": { + "score_default": 10, + "color": "#434343", # }, }, - - 'width':{ - '?':{ - 'score_default' : 10, - 'color' : '#434343', # + "width": { + "?": { + "score_default": 10, + "color": "#434343", # }, # in a future... }, - 'incline':{ - '?':{ - 'score_default' : 10, - 'color' : '#434343', # + "incline": { + "?": { + "score_default": 10, + "color": "#434343", # }, # in a future... }, - 'tactile_paving':{ - #CHECK KERBS + "tactile_paving": { + # CHECK KERBS }, - 'incline:across':{ - '?':{ - 'score_default' : 10, - 'color' : '#434343', # + "incline:across": { + "?": { + "score_default": 10, + "color": "#434343", # }, # in a future... - } + }, }, - - 'kerbs':{ - 'kerb':{ - 'raised':{ - 'score_default' : -30, - 'color' : '#000000', #black + "kerbs": { + "kerb": { + "raised": { + "score_default": -30, + "color": "#000000", # black }, - 'rolled':{ - 'score_default' : 0, - 'color' : '#808080', #50% gray + "rolled": { + "score_default": 0, + "color": "#808080", # 50% gray }, - 'no':{ - 'score_default' : 10, - 'color' : '#bebebe', #75% hray + "no": { + "score_default": 10, + "color": "#bebebe", # 75% hray }, - 'lowered':{ - 'score_default' : 50, - 'color' : '#ffffff', #white + "lowered": { + "score_default": 50, + "color": "#ffffff", # white }, - 'flush':{ - 'score_default' : 60, - 'color' : '#ffffff', #white + "flush": { + "score_default": 60, + "color": "#ffffff", # white }, - - '?':{ - 'score_default' : -10, # equivalent to "raised" - 'color' : '#d9d9d9', # + "?": { + "score_default": -10, # equivalent to "raised" + "color": "#d9d9d9", # }, - }, - 'tactile_paving':{ - 'yes':{ - 'score_default' : 100, - 'color' : '#6146d0', - - 'opacity' : 1, - }, - 'contrasted':{ - 'score_default' : 100, - 'color' : '#6146d0', - - 'opacity' : 1, - - }, - 'no':{ - 'score_default' : 0, - 'color' : '#bd1006', - - 'opacity' : 0, - + "tactile_paving": { + "yes": { + "score_default": 100, + "color": "#6146d0", + "opacity": 1, + }, + "contrasted": { + "score_default": 100, + "color": "#6146d0", + "opacity": 1, + }, + "no": { + "score_default": 0, + "color": "#bd1006", + "opacity": 0, + }, + "?": { + "score_default": 0, # equivalent to "no" + "color": "#717171", # "#434343", # + "opacity": 0, }, - - '?':{ - 'score_default' : 0, # equivalent to "no" - 'color' : "#717171",#"#434343", # - - 'opacity' : 0, - - }, - - } }, - - 'crossings':{ + }, + "crossings": { # default scores should be what was named "bonus" - 'crossing': { + "crossing": { # base color-scheme: ColorBrewer (thx!!) 12-class Paired - 'no':{ + "no": { # 'score_default' : 0, # 'bonus' : -100, - 'score_default' : -100, - - 'dasharray' :"0", - 'dashoffset': '0', - - 'color' : '#e31a1c', # RED - + "score_default": -100, + "dasharray": "0", + "dashoffset": "0", + "color": "#e31a1c", # RED }, - 'unmarked':{ + "unmarked": { # 'score_default' : 70, # 'bonus' : 0, - 'score_default' : 0, - + "score_default": 0, # may get help on: https://gigacore.github.io/demos/svg-stroke-dasharray-generator/ - - 'dasharray' :"5,10", - 'dashoffset': '0', - - - 'color' : '#ffff99', + "dasharray": "5,10", + "dashoffset": "0", + "color": "#ffff99", }, - 'marked':{ + "marked": { # 'score_default' : 90, # 'bonus' : 20, - - 'score_default' : 20, - - 'dasharray' :"0", - 'dashoffset': '0', - - - 'color' : '#a6cee3', + "score_default": 20, + "dasharray": "0", + "dashoffset": "0", + "color": "#a6cee3", }, - - 'zebra':{ + "zebra": { # 'score_default' : 90, # 'bonus' : 20, - - 'score_default' : 20, - - 'dasharray' :"0", - 'dashoffset': '0', - - - 'color' : '#a6cee3', + "score_default": 20, + "dasharray": "0", + "dashoffset": "0", + "color": "#a6cee3", }, - - 'uncontrolled':{ + "uncontrolled": { # 'score_default' : 100, # 'bonus' : 30, - - 'score_default' : 30, - - 'dasharray' :"0", - 'dashoffset': '0', - - - 'color' : '#a6cee3', + "score_default": 30, + "dasharray": "0", + "dashoffset": "0", + "color": "#a6cee3", }, - - 'traffic_signals':{ + "traffic_signals": { # 'score_default' : 100, # 'bonus' : 30, - - 'score_default' : 30, - - 'dasharray' :"0", - 'dashoffset': '0', - - 'color' : '#1f78b4', + "score_default": 30, + "dasharray": "0", + "dashoffset": "0", + "color": "#1f78b4", }, - - '?':{ + "?": { # 'score_default' : 10, # 'bonus' : 0, - - 'score_default' : 0, - - 'dasharray' :"0", - 'dashoffset': '0', - - - 'color' : 'gray', # + "score_default": 0, + "dasharray": "0", + "dashoffset": "0", + "color": "gray", # }, - - }, - 'surface':{ + "surface": { # CHECK SIDEWALKS - }, - 'smoothness':{ + "smoothness": { # CHECK SIDEWALKS - - }, - 'traffic_calming':{ - 'table':{ + "traffic_calming": { + "table": { # 'score_default' : 100, - 'score_default' : 20, - + "score_default": 20, # 'bonus' : 20, - 'color' : '#ffff99', + "color": "#ffff99", }, - - 'bump':{ + "bump": { # 'score_default' : 100, - 'score_default' : 20, - + "score_default": 20, # 'bonus' : 20, - 'color' : '#ffff99', + "color": "#ffff99", }, - - 'hump':{ + "hump": { # 'score_default' : 100, - 'score_default' : 20, - + "score_default": 20, # 'bonus' : 20, - 'color' : '#ffff99', + "color": "#ffff99", }, - - '?':{ - 'score_default' : 0, - 'color' : '#63636399', # + "?": { + "score_default": 0, + "color": "#63636399", # }, - - } -} + }, + }, } layernames = [key for key in fields_values_properties] # values to be copied: -fields_values_properties['sidewalks']['tactile_paving'] = fields_values_properties['kerbs']['tactile_paving'] +fields_values_properties["sidewalks"]["tactile_paving"] = fields_values_properties[ + "kerbs" +]["tactile_paving"] -fields_values_properties['crossings']['surface'] = fields_values_properties['sidewalks']['surface'] +fields_values_properties["crossings"]["surface"] = fields_values_properties[ + "sidewalks" +]["surface"] -fields_values_properties['crossings']['smoothness'] = fields_values_properties['sidewalks']['smoothness'] +fields_values_properties["crossings"]["smoothness"] = fields_values_properties[ + "sidewalks" +]["smoothness"] # required_fields: req_fields = { - 'sidewalks':['surface','smoothness','width','incline','tactile_paving','incline:across','osm_id','last_update'], - 'kerbs':['kerb','tactile_paving','osm_id','last_update'], - 'crossings':['crossing','surface','smoothness','traffic_calming','osm_id','last_update'], + "sidewalks": [ + "surface", + "smoothness", + "width", + "incline", + "tactile_paving", + "incline:across", + "osm_id", + "last_update", + ], + "kerbs": ["kerb", "tactile_paving", "osm_id", "last_update"], + "crossings": [ + "crossing", + "surface", + "smoothness", + "traffic_calming", + "osm_id", + "last_update", + ], } # a case of "smoothness=concrete:pĺates" demanded this -wrong_misspelled_values ={ - 'sidewalks':{ - 'smoothness':{'concrete:plates':'?'}, - 'surface':{'betão':'?','Petit_Pavê':'sett','porcelain tiles':'ceramic:tiles'} - }, - 'kerbs':{ - - }, - 'crossings':{ - - }, - 'other_footways':{ - +wrong_misspelled_values = { + "sidewalks": { + "smoothness": {"concrete:plates": "?"}, + "surface": { + "betão": "?", + "Petit_Pavê": "sett", + "porcelain tiles": "ceramic:tiles", + }, }, + "kerbs": {}, + "crossings": {}, + "other_footways": {}, } geom_type_dict = { - 'sidewalks':['LineString'], - 'crossings':['LineString'], - 'kerbs':['Point'], - 'other_footways':['LineString','Polygon','MultiPolygon'] + "sidewalks": ["LineString"], + "crossings": ["LineString"], + "kerbs": ["Point"], + "other_footways": ["LineString", "Polygon", "MultiPolygon"], } -all_layers_geom_types = {k:v[0] for k,v in geom_type_dict.items()} -del all_layers_geom_types['other_footways'] +all_layers_geom_types = {k: v[0] for k, v in geom_type_dict.items()} +del all_layers_geom_types["other_footways"] for subcategory in other_footways_geometry_types: all_layers_geom_types[subcategory] = other_footways_geometry_types[subcategory] -statistics_basepath = 'statistics' +statistics_basepath = "statistics" -# defined here to avoid circular importing problems -def get_url(relative_url,base_url=node_homepage_url): - return os.path.join(base_url,relative_url) +# defined here to avoid circular importing problems +def get_url(relative_url, base_url=node_homepage_url): + return os.path.join(base_url, relative_url) diff --git a/functions.py b/functions.py index b5f8ed2..8edbe6a 100644 --- a/functions.py +++ b/functions.py @@ -2,7 +2,7 @@ import bs4 from time import sleep, time import pandas as pd -from datetime import datetime +from datetime import datetime import json, requests from xml.etree import ElementTree import geopandas as gpd @@ -14,48 +14,53 @@ READ/ DUMP STUFF """ + + def read_json(inputpath): with open(inputpath) as reader: data = reader.read() return json.loads(data) - -def dump_json(inputdict,outputpath,indent=4): - with open(outputpath,'w+',encoding='utf8') as json_handle: - json.dump(inputdict,json_handle,indent=indent,ensure_ascii=False) -def file_as_string(inputpath:str): + +def dump_json(inputdict, outputpath, indent=4): + with open(outputpath, "w+", encoding="utf8") as json_handle: + json.dump(inputdict, json_handle, indent=indent, ensure_ascii=False) + + +def file_as_string(inputpath: str): if os.path.exists(inputpath): - with open(inputpath,encoding='utf8') as reader: + with open(inputpath, encoding="utf8") as reader: return reader.read() else: - raise(FileNotFoundError) - -def str_to_file(inputstr:str,outputpath:str,check_path=False): - if check_path: - if not os.path.exists(outputpath): - raise(FileNotFoundError) + raise (FileNotFoundError) + +def str_to_file(inputstr: str, outputpath: str, check_path=False): + if check_path: + if not os.path.exists(outputpath): + raise (FileNotFoundError) - with open(outputpath,'w+',encoding='utf8') as writer: + with open(outputpath, "w+", encoding="utf8") as writer: writer.write(inputstr) sleep(0.1) + class fileAsStrHandler: - def __init__(self,inputpath:str): + def __init__(self, inputpath: str): self.path = inputpath self.content = file_as_string(self.path) - def simple_replace(self,original_part,new_part=''): + def simple_replace(self, original_part, new_part=""): """default is empty for just remove the selected content""" - self.content = self.content.replace(original_part,new_part) + self.content = self.content.replace(original_part, new_part) def rewrite(self): - str_to_file(self.content,self.path) - - def write_to_another_path(self,outputpath): - str_to_file(self.content,outputpath) + str_to_file(self.content, self.path) + + def write_to_another_path(self, outputpath): + str_to_file(self.content, outputpath) """ @@ -64,30 +69,30 @@ def write_to_another_path(self,outputpath): """ + def formatted_datetime_now(): now = datetime.now() return now.strftime("%d/%m/%Y %H:%M:%S") - -def record_datetime(key,json_path='data/last_updated.json'): +def record_datetime(key, json_path="data/last_updated.json"): datadict = read_json(json_path) datadict[key] = formatted_datetime_now() - dump_json(datadict,json_path) + dump_json(datadict, json_path) + + sleep(0.1) - sleep(.1) -def record_to_json(key,obj,json_path): +def record_to_json(key, obj, json_path): datadict = read_json(json_path) datadict[key] = obj - dump_json(datadict,json_path) - + dump_json(datadict, json_path) """ @@ -119,10 +124,12 @@ def record_to_json(key,obj,json_path): """ -def gen_updating_infotable_page(outpath='data/data_updating.html',json_path='data/last_updated.json'): +def gen_updating_infotable_page( + outpath="data/data_updating.html", json_path="data/last_updated.json" +): - tablepart = '' + tablepart = "" records_dict = read_json(json_path) @@ -131,7 +138,7 @@ def gen_updating_infotable_page(outpath='data/data_updating.html',json_path='dat {key}{records_dict[key]} """ - page_as_txt = f''' + page_as_txt = f""" @@ -190,17 +197,27 @@ def gen_updating_infotable_page(outpath='data/data_updating.html',json_path='dat - ''' + """ # with open(outpath,'w+') as writer: # writer.write(page_as_txt) - - str_to_file(page_as_txt,outpath) + str_to_file(page_as_txt, outpath) -def gen_quality_report_page_and_files(outpath,tabledata,feat_type,category,quality_category,text,occ_type,csvpath,count_page=False): - pagename_base = f'{quality_category}_{category}' +def gen_quality_report_page_and_files( + outpath, + tabledata, + feat_type, + category, + quality_category, + text, + occ_type, + csvpath, + count_page=False, +): + + pagename_base = f"{quality_category}_{category}" csv_url = f"""

@@ -215,7 +232,6 @@ def gen_quality_report_page_and_files(outpath,tabledata,feat_type,category,quali commentary """ - if count_page: tablepart = f""" OSM ID (link) @@ -225,37 +241,36 @@ def gen_quality_report_page_and_files(outpath,tabledata,feat_type,category,quali valid_featcount = 0 - with open(csvpath,'w+') as file: - writer = csv.writer(file,delimiter=',',quotechar='"') - writer.writerow(['osm_id','key','value','commentary']) + with open(csvpath, "w+") as file: + writer = csv.writer(file, delimiter=",", quotechar='"') + writer.writerow(["osm_id", "key", "value", "commentary"]) + + for line in tabledata: + try: + line_as_str = "" + if line: + if len(line) > 2: + if not pd.isna(line[2]): + + writer.writerow(line) - for line in tabledata: - try: - line_as_str = '' - if line: - if len(line)> 2: - if not pd.isna(line[2]): + line[0] = return_weblink_V2(str(line[0]), feat_type) - writer.writerow(line) + line_as_str += "" - line[0] = return_weblink_V2(str(line[0]),feat_type) - - line_as_str += "" - - for element in line: - line_as_str += f"{str(element)}" - - line_as_str += "\n" + for element in line: + line_as_str += f"{str(element)}" - tablepart += line_as_str + line_as_str += "\n" - valid_featcount += 1 - except: - if line: - print('skipped',line) + tablepart += line_as_str + valid_featcount += 1 + except: + if line: + print("skipped", line) - with open(outpath,'w+') as writer: + with open(outpath, "w+") as writer: page = f""" @@ -304,40 +319,45 @@ def gen_quality_report_page_and_files(outpath,tabledata,feat_type,category,quali return valid_featcount - def find_map_ref(input_htmlpath): with open(input_htmlpath) as inf: txt = inf.read() - soup = bs4.BeautifulSoup(txt,features='html5lib') + soup = bs4.BeautifulSoup(txt, features="html5lib") - refs = soup.find_all(attrs={'class':"folium-map"}) + refs = soup.find_all(attrs={"class": "folium-map"}) for found_ref in refs: - return found_ref['id'] - - + return found_ref["id"] -def find_html_name(input_htmlpath,specific_ref,tag_ref='img',specific_tag='src',identifier='id'): +def find_html_name( + input_htmlpath, specific_ref, tag_ref="img", specific_tag="src", identifier="id" +): with open(input_htmlpath) as inf: txt = inf.read() - soup = bs4.BeautifulSoup(txt,features='html5lib') + soup = bs4.BeautifulSoup(txt, features="html5lib") refs = soup.find_all(tag_ref) - for found_ref in refs: # if specific_tag in found_ref: if found_ref[specific_tag] == specific_ref: return found_ref[identifier] - -def style_changer(in_out_htmlpath,element_key,key='style',original='bottom',new='top',append_t=None): + +def style_changer( + in_out_htmlpath, + element_key, + key="style", + original="bottom", + new="top", + append_t=None, +): with open(in_out_htmlpath) as inf: txt = inf.read() - soup = bs4.BeautifulSoup(txt,features='html5lib') + soup = bs4.BeautifulSoup(txt, features="html5lib") style_refs = soup.find_all(key) @@ -346,7 +366,7 @@ def style_changer(in_out_htmlpath,element_key,key='style',original='bottom',new= if element_key in as_txt: if new: - new_text = as_txt.replace(original,new) + new_text = as_txt.replace(original, new) else: new_text = as_txt @@ -355,54 +375,53 @@ def style_changer(in_out_htmlpath,element_key,key='style',original='bottom',new= break - - with open(in_out_htmlpath,'w+', encoding='utf-8') as writer: - writer.write(str(soup).replace(as_txt,new_text)) + with open(in_out_htmlpath, "w+", encoding="utf-8") as writer: + writer.write(str(soup).replace(as_txt, new_text)) sleep(0.2) - -def add_to_page_after_first_tag(html_filepath,element_string,tag_or_txt='',count=1): - ''' - Quick and dirty way to insert some stuff directly on the webpage + +def add_to_page_after_first_tag( + html_filepath, element_string, tag_or_txt="", count=1 +): + """ + Quick and dirty way to insert some stuff directly on the webpage Originally intended only for beware of tags that repeat! the "count" argument is very important! - ''' - + """ with open(html_filepath) as reader: pag_txt = reader.read() - replace_text = f'{tag_or_txt} \n{element_string}\n' + replace_text = f"{tag_or_txt} \n{element_string}\n" + + with open(html_filepath, "w+") as writer: + writer.write(pag_txt.replace(tag_or_txt, replace_text, count)) - - with open(html_filepath,'w+') as writer: - writer.write(pag_txt.replace(tag_or_txt,replace_text,count)) + sleep(0.1) - sleep(.1) -def replace_at_html(html_filepath,original_text,new_text,count=1): - ''' - Quick and dirty way to replace some stuff directly on the webpage +def replace_at_html(html_filepath, original_text, new_text, count=1): + """ + Quick and dirty way to replace some stuff directly on the webpage Originally intended only for beware of tags that repeat! the "count" argument is very important! - ''' + """ if os.path.exists(html_filepath): with open(html_filepath) as reader: pag_txt = reader.read() - - with open(html_filepath,'w+') as writer: - writer.write(pag_txt.replace(original_text,new_text,count)) + with open(html_filepath, "w+") as writer: + writer.write(pag_txt.replace(original_text, new_text, count)) else: - raise('Error: file not found!!') + raise ("Error: file not found!!") - sleep(.1) + sleep(0.1) # def file_to_str(filepath): @@ -412,13 +431,21 @@ def replace_at_html(html_filepath,original_text,new_text,count=1): # return pag_txt -def find_between_strings(string, start, end,return_unique=True,exclusions:list=None,include_linebreaks=False): + +def find_between_strings( + string, + start, + end, + return_unique=True, + exclusions: list = None, + include_linebreaks=False, +): pattern = f"{start}(.*){end}" # print(pattern) if include_linebreaks: - matches = re.findall(pattern, string,re.DOTALL) + matches = re.findall(pattern, string, re.DOTALL) else: - matches = re.findall(pattern, string) + matches = re.findall(pattern, string) if return_unique: matches = list(set(matches)) @@ -430,68 +457,89 @@ def find_between_strings(string, start, end,return_unique=True,exclusions:list=N # (geo)Pandas stuff: -def get_score_df(inputdict,category='sidewalks',osm_key='surface',input_field='score_default',output_field_base='score'): +def get_score_df( + inputdict, + category="sidewalks", + osm_key="surface", + input_field="score_default", + output_field_base="score", +): - output_field_name = f'{category}_{osm_key}_{output_field_base}' - dict = {osm_key:[],output_field_name:[]} + output_field_name = f"{category}_{osm_key}_{output_field_base}" + dict = {osm_key: [], output_field_name: []} for val_key in inputdict[category][osm_key]: dict[osm_key].append(val_key) - dict[output_field_name].append(inputdict[category][osm_key][val_key][input_field]) + dict[output_field_name].append( + inputdict[category][osm_key][val_key][input_field] + ) - return pd.DataFrame(dict), output_field_name + return pd.DataFrame(dict), output_field_name -def get_attr_dict(inputdict,category='sidewalks',osm_tag='surface',attr='color'): +def get_attr_dict(inputdict, category="sidewalks", osm_tag="surface", attr="color"): color_dict = {} for tag_value in inputdict[category][osm_tag]: color_dict[tag_value] = inputdict[category][osm_tag][tag_value][attr] return color_dict + def return_weblink_way(string_id): return f"{string_id}" + def return_weblink_node(string_id): return f"{string_id}" -def return_weblink_V2(string_id,featuretype): + +def return_weblink_V2(string_id, featuretype): return f"{string_id}" + def return_weblink_V3(type_id_string): - featuretype,string_id = type_id_string.split('_') + featuretype, string_id = type_id_string.split("_") return f"{string_id}" -''' + +""" HISTORY STUFF -''' +""" + -def get_feature_history_url(featureid,type='way'): - return f'https://www.openstreetmap.org/api/0.6/{type}/{featureid}/history' +def get_feature_history_url(featureid, type="way"): + return f"https://www.openstreetmap.org/api/0.6/{type}/{featureid}/history" -def parse_datetime_str(inputstr,format='ymdhms'): + +def parse_datetime_str(inputstr, format="ymdhms"): format_dict = { - 'ymdhms' : '%Y-%m-%dT%H:%M:%S', + "ymdhms": "%Y-%m-%dT%H:%M:%S", } - return datetime.strptime(inputstr,format_dict[format]) + return datetime.strptime(inputstr, format_dict[format]) -def get_datetime_last_update(featureid,featuretype='way',onlylast=True,return_parsed=True,return_special_tuple=True): +def get_datetime_last_update( + featureid, + featuretype="way", + onlylast=True, + return_parsed=True, + return_special_tuple=True, +): - h_url = get_feature_history_url(featureid,featuretype) + h_url = get_feature_history_url(featureid, featuretype) try: response = requests.get(h_url) except: if onlylast: if return_parsed and return_special_tuple: - return [None]*4 #4 Nones + return [None] * 4 # 4 Nones - return '' + return "" else: return [] @@ -501,20 +549,19 @@ def get_datetime_last_update(featureid,featuretype='way',onlylast=True,return_pa element_list = tree.findall(featuretype) if element_list: - date_rec = [element.attrib['timestamp'][:-1] for element in element_list] + date_rec = [element.attrib["timestamp"][:-1] for element in element_list] if onlylast: if return_parsed: if return_special_tuple: # parsed = datetime.strptime(date_rec[-1],'%Y-%m-%dT%H:%M:%S') parsed = parse_datetime_str(date_rec[-1]) - return len(date_rec),parsed.day,parsed.month,parsed.year + return len(date_rec), parsed.day, parsed.month, parsed.year else: # return datetime.strptime(date_rec[-1],'%Y-%m-%dT%H:%M:%S') return parse_datetime_str(date_rec[-1]) - else: return date_rec[-1] @@ -525,42 +572,49 @@ def get_datetime_last_update(featureid,featuretype='way',onlylast=True,return_pa else: return date_rec - else: if onlylast: - return '' + return "" else: return [] - + else: - print('bad request, check feature id/type') + print("bad request, check feature id/type") if onlylast: - return '' + return "" else: return [] def get_datetime_last_update_node(featureid): # all default options - return get_datetime_last_update(featureid,featuretype='node') + return get_datetime_last_update(featureid, featuretype="node") -def print_relevant_columnamesV2(input_df,not_include=('score','geometry','type','id'),outfilepath=None): +def print_relevant_columnamesV2( + input_df, not_include=("score", "geometry", "type", "id"), outfilepath=None +): - as_list = [column for column in input_df.columns if not any(word in column for word in not_include)] + as_list = [ + column + for column in input_df.columns + if not any(word in column for word in not_include) + ] # print(*as_list) if outfilepath: - with open(outfilepath,'w+') as writer: - writer.write(','.join(as_list)) + with open(outfilepath, "w+") as writer: + writer.write(",".join(as_list)) return as_list -def check_if_wikipage_exists(name,category="Key:",wiki_page='https://wiki.openstreetmap.org/wiki/'): +def check_if_wikipage_exists( + name, category="Key:", wiki_page="https://wiki.openstreetmap.org/wiki/" +): - url = f'{wiki_page}{category}{name}' + url = f"{wiki_page}{category}{name}" while True: try: @@ -571,58 +625,71 @@ def check_if_wikipage_exists(name,category="Key:",wiki_page='https://wiki.openst return status == 200 + """ geopandas """ -def gdf_to_js_file(input_gdf,output_path,output_varname): + +def gdf_to_js_file(input_gdf, output_path, output_varname): """ - this function converts a geopandas dataframe to a javascript file, was the only thing that worked for vectorGrid module + this function converts a geopandas dataframe to a javascript file, was the only thing that worked for vectorGrid module - returns the importing to be included in the html file + returns the importing to be included in the html file """ input_gdf.to_file(output_path) - as_str = f"{output_varname} = "+ file_as_string(output_path) + as_str = f"{output_varname} = " + file_as_string(output_path) - str_to_file(as_str,output_path) + str_to_file(as_str, output_path) return f'' -def create_length_field(input_gdf,fieldname='length(km)',in_km=True): + +def create_length_field(input_gdf, fieldname="length(km)", in_km=True): factor = 1 if in_km: factor = 1000 utm_crs = input_gdf.estimate_utm_crs() - input_gdf['length(km)'] = input_gdf.to_crs(utm_crs).length/factor + input_gdf["length(km)"] = input_gdf.to_crs(utm_crs).length / factor + -def create_weblink_field(input_gdf,featuretype='LineString',inputfield='id',fieldname='weblink'): - if featuretype == 'LineString': - input_gdf[fieldname] = input_gdf[inputfield].astype('string').apply(return_weblink_way) - if featuretype == 'Point': - input_gdf[fieldname] = input_gdf[inputfield].astype('string').apply(return_weblink_node) +def create_weblink_field( + input_gdf, featuretype="LineString", inputfield="id", fieldname="weblink" +): + if featuretype == "LineString": + input_gdf[fieldname] = ( + input_gdf[inputfield].astype("string").apply(return_weblink_way) + ) + if featuretype == "Point": + input_gdf[fieldname] = ( + input_gdf[inputfield].astype("string").apply(return_weblink_node) + ) def create_folder_if_not_exists(folderpath): if not os.path.exists(folderpath): os.makedirs(folderpath) + def create_folderlist(folderlist): for folder in folderlist: create_folder_if_not_exists(folder) + def remove_if_exists(pathfile): if os.path.exists(pathfile): os.remove(pathfile) + def listdir_fullpath(path): return [os.path.join(path, file) for file in os.listdir(path)] -def get_territory_polygon(place_name,outpath=None,outpath_metadata=None): +def get_territory_polygon(place_name, outpath=None, outpath_metadata=None): """ This function takes a place name as input and retrieves the corresponding territory polygon using the Nominatim API. It can also optionally save the polygon as a GeoJSON file. @@ -641,37 +708,40 @@ def get_territory_polygon(place_name,outpath=None,outpath_metadata=None): # Parse the response as a JSON object data = response.json() - # sort data by "importance", that is a key in each dictionary of the list: data.sort(key=lambda x: x["importance"], reverse=True) # Get the polygon of the territory as a GeoJSON object - polygon = data[0]['geojson'] + polygon = data[0]["geojson"] if outpath: dump_json(polygon, outpath) if outpath_metadata: - if 'geojson' in data[0]: - del data[0]['geojson'] + if "geojson" in data[0]: + del data[0]["geojson"] dump_json(data[0], outpath_metadata) # Return the polygon return polygon + def geodataframe_from_a_geometry(geometry): return gpd.GeoDataFrame(geometry=[geometry]) -def bbox_geodataframe(bbox,resort=True): + +def bbox_geodataframe(bbox, resort=True): if resort: bbox = resort_bbox(bbox) return gpd.GeoDataFrame(geometry=[box(*bbox)]) + def resort_bbox(bbox): - return [bbox[1],bbox[0],bbox[3],bbox[2]] + return [bbox[1], bbox[0], bbox[3], bbox[2]] + def merge_list_of_dictionaries(list_of_dicts): merged_dict = {} @@ -686,29 +756,34 @@ def merge_list_of_dictionaries(list_of_dicts): else: merged_dict[key].append(value) else: - merged_dict[key] = value if not isinstance(value, list) else value.copy() + merged_dict[key] = ( + value if not isinstance(value, list) else value.copy() + ) return merged_dict + def join_to_node_homepage(input_list_or_str): - if isinstance(input_list_or_str,list): - return os.path.join(node_homepage_url,*input_list_or_str) + if isinstance(input_list_or_str, list): + return os.path.join(node_homepage_url, *input_list_or_str) else: - return os.path.join(node_homepage_url,input_list_or_str) + return os.path.join(node_homepage_url, input_list_or_str) + def save_geoparquet(input_gdf, outpath): """ - Saves a GeoDataFrame to a Parquet file. + Saves a GeoDataFrame to a Parquet file. If the GeoDataFrame is empty, creates an empty Parquet file. Workaround for: https://github.com/geopandas/geopandas/issues/3137 """ if input_gdf.empty: - gpd.GeoDataFrame(columns=['geometry']).to_parquet(outpath) + gpd.GeoDataFrame(columns=["geometry"]).to_parquet(outpath) else: input_gdf.to_parquet(outpath) -def row_query(df, querydict, mode='any',reverse=False): + +def row_query(df, querydict, mode="any", reverse=False): """ Apply a query to each row in a DataFrame and return a boolean result. @@ -729,37 +804,60 @@ def row_query(df, querydict, mode='any',reverse=False): 2 False dtype: bool """ - if mode == 'any': - selection = df.isin(querydict).any(axis=1) - elif mode == 'all': - selection = df.isin(querydict).all(axis=1) - + if mode == "any": + selection = df.isin(querydict).any(axis=1) + elif mode == "all": + selection = df.isin(querydict).all(axis=1) + if reverse: return ~selection else: return selection - + + def get_gdfs_dict(raw_data=False): # used dict: paths_dict - category_group = 'data_raw' if raw_data else 'data' + category_group = "data_raw" if raw_data else "data" + + return { + category: gpd.read_parquet(paths_dict[category_group][category]) + for category in paths_dict[category_group] + } - return {category: gpd.read_parquet(paths_dict[category_group][category]) for category in paths_dict[category_group]} -def get_gdfs_dict_v2(category='data'): +def get_gdfs_dict_v2(category="data"): """ available categories: 'data', 'data_raw','other_footways_subcategories', 'map_layers' """ - return {category: gpd.read_parquet(paths_dict[category][category]) for category in paths_dict[category]} + return { + category: gpd.read_parquet(paths_dict[category][category]) + for category in paths_dict[category] + } + -def remove_empty_columns(gdf,report=False): +def remove_empty_columns(gdf, report=False): if report: prev = len(gdf.columns) - gdf.dropna(axis='columns',how='all',inplace=True) + gdf.dropna(axis="columns", how="all", inplace=True) if report: - print(f' removed {prev-len(gdf.columns)} empty columns') - + print(f" removed {prev-len(gdf.columns)} empty columns") + + def get_boundaries_bbox(): - return list(gpd.read_file(boundaries_geojson_path).total_bounds) \ No newline at end of file + return list(gpd.read_file(boundaries_geojson_path).total_bounds) + + +def rename_dict_key( + dictionary, old_key, new_key, ignore_missing=True, ignore_existing=True +): + if old_key in dictionary and not ignore_missing: + raise KeyError(f"Key {old_key} not found in dictionary") + + if new_key in dictionary and not ignore_existing: + raise KeyError(f"Key {new_key} already exists in dictionary") + + if old_key in dictionary: + dictionary[new_key] = dictionary.pop(old_key) diff --git a/webmap/create_webmap_new.py b/webmap/create_webmap_new.py index 0a02ffe..e87b39c 100644 --- a/webmap/create_webmap_new.py +++ b/webmap/create_webmap_new.py @@ -3,7 +3,7 @@ # create a --development flag: parser = argparse.ArgumentParser() -parser.add_argument('--development', action='store_true') +parser.add_argument("--development", action="store_true") args = parser.parse_args() in_dev = args.development @@ -12,16 +12,16 @@ params = read_json(webmap_params_original_path) # then override and fill in with the stuff: -params['data_layers'] = MAP_DATA_LAYERS +params["data_layers"] = MAP_DATA_LAYERS # the layers that by type: -params['layer_types'] = layer_type_groups +params["layer_types"] = layer_type_groups # boundaries: -params['bounds'] = get_boundaries_bbox() +params["bounds"] = get_boundaries_bbox() # updating the node's url: -params['node_url'] = node_homepage_url +params["node_url"] = node_homepage_url # # generating the "sources" and layernames: params.update(get_sources(only_urls=True)) @@ -30,36 +30,40 @@ # very temporary: # params['sources'] = MAP_SOURCES -params['styles'] = { - "footway_categories" : create_base_style(), - 'crossings_and_kerbs' : create_crossings_kerbs_style() +params["styles"] = { + "footway_categories": create_base_style(), + "crossings_and_kerbs": create_crossings_kerbs_style(), } interest_attributes = { # key is raw attribute name, value is label (human readable) - "surface" : "Surface", - "smoothness" : "Smoothness", - "tactile_paving" : "Tactile Paving", - 'lit' : "Lighting", - 'traffic_calming' : "Traffic Calming", - "wheelchair" : 'wheelchair=* tag', + "surface": "Surface", + "smoothness": "Smoothness", + "tactile_paving": "Tactile Paving", + "lit": "Lighting", + "traffic_calming": "Traffic Calming", + "wheelchair": "wheelchair=* tag", } attribute_layers = { # default is "sidewalks", only specified if different: - 'traffic_calming' : 'crossings', + "traffic_calming": "crossings", } different_else_color = { # default is "gray", specifyed ony if different: - 'traffic_calming' : '#63636366', + "traffic_calming": "#63636366", } for attribute in interest_attributes: - color_dict = get_color_dict(attribute,attribute_layers.get(attribute,'sidewalks')) - color_schema = create_maplibre_color_schema(color_dict,attribute,different_else_color.get(attribute,'gray')) - - params['styles'][attribute] = create_simple_map_style(interest_attributes[attribute],color_schema,color_dict,attribute) + color_dict = get_color_dict(attribute, attribute_layers.get(attribute, "sidewalks")) + color_schema = create_maplibre_color_schema( + color_dict, attribute, different_else_color.get(attribute, "gray") + ) + + params["styles"][attribute] = create_simple_map_style( + interest_attributes[attribute], color_schema, color_dict, attribute + ) # reading the base html webmap_html = file_as_string(webmap_base_path) @@ -67,9 +71,9 @@ # doing other stuff like insertions and nasty things (TODO): # finally generate the files: -str_to_file(webmap_html,webmap_path) -dump_json(params,webmap_params_path) +str_to_file(webmap_html, webmap_path) +dump_json(params, webmap_params_path) # if we are in dev mode, also dump the original params: if in_dev: - dump_json(params,webmap_params_original_path) \ No newline at end of file + dump_json(params, webmap_params_original_path) diff --git a/webmap/standalone_legend.py b/webmap/standalone_legend.py index 7c46d51..d57520b 100644 --- a/webmap/standalone_legend.py +++ b/webmap/standalone_legend.py @@ -3,6 +3,9 @@ from matplotlib.lines import Line2D from matplotlib.patches import Patch +from functions import * + + # thx ChatGPT, employed to transform a scratch code into the class class StandaloneLegend: def __init__(self): @@ -11,7 +14,7 @@ def __init__(self): self.legend_labels = [] self.legendFig = plt.figure("Legend plot") - def add_line(self, label='Line', **kwargs): + def add_line(self, label="Line", **kwargs): """ Add a line to the legend with customizable parameters. @@ -19,11 +22,14 @@ def add_line(self, label='Line', **kwargs): label (str): The label for the line element. **kwargs: Additional keyword arguments for Line2D. """ + + rename_dict_key(kwargs, "width", "linewidth") + line = Line2D([0], [0], label=label, **kwargs) self.legend_elements.append(line) self.legend_labels.append(label) - def add_marker(self, marker='o', label='Marker', **kwargs): + def add_marker(self, marker="o", label="Marker", **kwargs): """ Add a marker to the legend with customizable parameters. @@ -32,13 +38,19 @@ def add_marker(self, marker='o', label='Marker', **kwargs): label (str): The label for the marker element. **kwargs: Additional keyword arguments for Line2D. """ + + # to standardize the key names + rename_dict_key(kwargs, "color", "markerfacecolor") + rename_dict_key(kwargs, "width", "markersize") + # Add default transparent color to kwargs if not provided - kwargs.setdefault('color', (0.0, 0.0, 0.0, 0.0)) + kwargs.setdefault("color", (0.0, 0.0, 0.0, 0.0)) + kwargs.setdefault("markeredgecolor", (0.0, 0.0, 0.0, 0.0)) marker = Line2D([0], [0], marker=marker, label=label, **kwargs) self.legend_elements.append(marker) self.legend_labels.append(label) - def add_patch(self, facecolor='orange', edgecolor='w', label='Patch', **kwargs): + def add_patch(self, facecolor="orange", edgecolor="w", label="Patch", **kwargs): """ Add a patch to the legend with customizable parameters. @@ -48,6 +60,12 @@ def add_patch(self, facecolor='orange', edgecolor='w', label='Patch', **kwargs): label (str): The label for the patch element. **kwargs: Additional keyword arguments for Patch. """ + + # rename_dict_key(kwargs, "facecolor", "markerfacecolor") + + if "width" in kwargs: + kwargs.pop("width") + patch = Patch(facecolor=facecolor, edgecolor=edgecolor, label=label, **kwargs) self.legend_elements.append(patch) self.legend_labels.append(label) @@ -61,15 +79,17 @@ def add_element(self, element_type, label, **kwargs): label (str): The label for the element. **kwargs: Additional keyword arguments for the element. """ - if element_type == 'line': + if element_type == "line": self.add_line(label=label, **kwargs) - elif element_type == 'marker' or element_type == 'circle': + elif element_type == "marker" or element_type == "circle": self.add_marker(label=label, **kwargs) - elif element_type == 'patch' or element_type == 'fill': + elif element_type == "patch" or element_type == "fill": self.add_patch(label=label, **kwargs) else: - raise ValueError(f"Unknown element type: {element_type}. Supported types are 'line', 'marker'/'circle', and 'patch'/'fill'.") - + raise ValueError( + f"Unknown element type: {element_type}. Supported types are 'line', 'marker'/'circle', and 'patch'/'fill'." + ) + def add_elements(self, elements): """ Add multiple custom elements to the legend. @@ -84,12 +104,15 @@ def __hash__(self) -> int: # enable hashing of the object, example: legend = StandaloneLegend(); hash(legend) return hash((self.legend_elements, self.legend_labels)) - def export(self, filename='legend.png'): + def export(self, filename="legend.png"): # Export the legend to an image file - self.legendFig.legend(handles=self.legend_elements, labels=self.legend_labels, loc='center') - self.legendFig.savefig(filename, bbox_inches='tight', transparent=True) + self.legendFig.legend( + handles=self.legend_elements, labels=self.legend_labels, loc="center" + ) + self.legendFig.savefig(filename, bbox_inches="tight", transparent=True) plt.close(self.legendFig) # Close the figure to free memory + # # Example usage: # legend = StandaloneLegend() # legend.add_line(color='b', linewidth=4, label='Line') diff --git a/webmap/webmap_lib.py b/webmap/webmap_lib.py index b95514b..4cdef60 100644 --- a/webmap/webmap_lib.py +++ b/webmap/webmap_lib.py @@ -1,87 +1,82 @@ import sys -sys.path.append('oswm_codebase') -from functions import * -from copy import deepcopy +sys.path.append("oswm_codebase") +from copy import deepcopy from standalone_legend import * -MAP_DATA_LAYERS = [l for l in paths_dict['map_layers']] + +MAP_DATA_LAYERS = [l for l in paths_dict["map_layers"]] # webmap stuff: -BASEMAP_URL = 'https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png' -webmap_params_original_path = 'oswm_codebase/webmap/webmap_params.json' -webmap_params_path = 'webmap_params.json' -webmap_base_path = 'oswm_codebase/webmap/webmap_base.html' -webmap_path = 'map.html' +BASEMAP_URL = "https://a.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png" +webmap_params_original_path = "oswm_codebase/webmap/webmap_params.json" +webmap_params_path = "webmap_params.json" +webmap_base_path = "oswm_codebase/webmap/webmap_base.html" +webmap_path = "map.html" -assets_path = 'oswm_codebase/assets/' -map_symbols_assets_path = os.path.join(assets_path, 'map_symbols') +assets_path = "oswm_codebase/assets/" +map_symbols_assets_path = os.path.join(assets_path, "map_symbols") # mapping geometry types to maplibre style map_geom_type_mapping = { - 'Polygon':'fill', - 'LineString':'line', - 'Point':'circle', - 'MultiPolygon':'fill', - 'MultiLineString':'line', - 'MultiPoint':'circle' - } + "Polygon": "fill", + "LineString": "line", + "Point": "circle", + "MultiPolygon": "fill", + "MultiLineString": "line", + "MultiPoint": "circle", +} # types for each layer: -layertypes_dict = { k: map_geom_type_mapping[v] for k,v in all_layers_geom_types.items() } +layertypes_dict = { + k: map_geom_type_mapping[v] for k, v in all_layers_geom_types.items() +} # the layers by type: -line_layers = [l for l in MAP_DATA_LAYERS if layertypes_dict[l] == 'line'] -fill_layers = [l for l in MAP_DATA_LAYERS if layertypes_dict[l] == 'fill'] -circle_layers = [l for l in MAP_DATA_LAYERS if layertypes_dict[l] == 'circle'] +line_layers = [l for l in MAP_DATA_LAYERS if layertypes_dict[l] == "line"] +fill_layers = [l for l in MAP_DATA_LAYERS if layertypes_dict[l] == "fill"] +circle_layers = [l for l in MAP_DATA_LAYERS if layertypes_dict[l] == "circle"] layer_type_groups = { # the order in this dict determines the order in the webmap: - 'fill':fill_layers, - 'line':line_layers, - 'circle':circle_layers + "fill": fill_layers, + "line": line_layers, + "circle": circle_layers, } # immutable layers, among different styles: -immutable_layers= [{ - "id": "osm-baselayer", - "source": "osm", - "type": "raster", - "paint": { - "raster-opacity": .9 - } - }, - { - "id": "boundaries", - "type": "line", - "source": "boundaries", - "paint": { - "line-color": "white", - "line-opacity": 0.4 - } - }] +immutable_layers = [ + { + "id": "osm-baselayer", + "source": "osm", + "type": "raster", + "paint": {"raster-opacity": 0.9}, + }, + { + "id": "boundaries", + "type": "line", + "source": "boundaries", + "paint": {"line-color": "white", "line-opacity": 0.4}, + }, +] # base dict for a map style: -mapstyle_basedict = { - "version": 8, - "sources": {}, - "layers": [] -} +mapstyle_basedict = {"version": 8, "sources": {}, "layers": []} # base_dicts for each layer type: - # "id": "pedestrian_areas", - # "source": "oswm_pmtiles_pedestrian_areas", - # "source-layer": "pedestrian_areas", - # "type": "fill", - # "paint": { - # "fill-color": "gray", - # "fill-opacity": 0.5 - # } +# "id": "pedestrian_areas", +# "source": "oswm_pmtiles_pedestrian_areas", +# "source-layer": "pedestrian_areas", +# "type": "fill", +# "paint": { +# "fill-color": "gray", +# "fill-opacity": 0.5 +# } layertypes_basedict = { - 'line':{ + "line": { "id": "", "source": "", "source-layer": "", @@ -89,15 +84,14 @@ "paint": { "line-color": "steelblue", "line-width": [ - 'case', - ['boolean', ['feature-state', 'hover'], False], - 6, - 3 - ] - - } + "case", + ["boolean", ["feature-state", "hover"], False], + 6, + 3, + ], + }, }, - 'fill':{ + "fill": { "id": "", "source": "", "source-layer": "", @@ -105,14 +99,14 @@ "paint": { "fill-color": "steelblue", "fill-opacity": [ - 'case', - ['boolean', ['feature-state', 'hover'], False], - 0.8, - 0.5 - ] - } + "case", + ["boolean", ["feature-state", "hover"], False], + 0.8, + 0.5, + ], + }, }, - 'circle':{ + "circle": { "id": "", "source": "", "source-layer": "", @@ -122,257 +116,294 @@ "circle-color": "steelblue", "circle-opacity": 0.8, "circle-radius": [ - 'case', - ['boolean', ['feature-state', 'hover'], False], - 7, - 4 - ] - } - } + "case", + ["boolean", ["feature-state", "hover"], False], + 7, + 4, + ], + }, + }, } -color_attribute = { - 'fill':'fill-color', - 'line':'line-color', - 'circle':'circle-color' -} +color_attribute = {"fill": "fill-color", "line": "line-color", "circle": "circle-color"} + -def get_sources(terrain_url=None,only_urls=False): +def get_sources(terrain_url=None, only_urls=False): ret = {} - ret['sources'] = {} - - for layername in paths_dict['map_layers']: - ret[f'{layername}_url'] = f'{node_homepage_url}data/tiles/{layername}.pmtiles' - - ret['sources'][f'oswm_pmtiles_{layername}'] = { + ret["sources"] = {} + + for layername in paths_dict["map_layers"]: + ret[f"{layername}_url"] = f"{node_homepage_url}data/tiles/{layername}.pmtiles" + + ret["sources"][f"oswm_pmtiles_{layername}"] = { "type": "vector", "url": f"pmtiles://{ret[f'{layername}_url']}", - "promoteId":"id", - "attribution": r'© OpenStreetMap Contributors'} - - ret['boundaries_url'] = f'{node_homepage_url}data/boundaries.geojson' + "promoteId": "id", + "attribution": r'© OpenStreetMap Contributors', + } + ret["boundaries_url"] = f"{node_homepage_url}data/boundaries.geojson" # basemap: - ret['sources']['osm'] = { + ret["sources"]["osm"] = { "type": "raster", "tiles": [BASEMAP_URL], - "attribution": r'© OpenStreetMap Contributors; basemap by CARTO' + "attribution": r'© OpenStreetMap Contributors; basemap by CARTO', } # boundaries: - ret['sources']['boundaries'] = { + ret["sources"]["boundaries"] = { "type": "geojson", - "data": ret['boundaries_url'], - "attribution": r'© OpenStreetMap Contributors' + "data": ret["boundaries_url"], + "attribution": r'© OpenStreetMap Contributors', } - + if terrain_url: # # # terrain: - ret['sources']['terrain'] = { + ret["sources"]["terrain"] = { "type": "raster-dem", "url": terrain_url, - "tileSize": 256 + "tileSize": 256, } - + if only_urls: - del ret['sources'] + del ret["sources"] return ret else: return ret - -MAP_SOURCES = get_sources()['sources'] - + + +MAP_SOURCES = get_sources()["sources"] + + def sort_keys_by_order(input_dict, order_list): ordered_keys = [] remaining_keys = list(input_dict.keys()) - + for order in order_list: for key in list(remaining_keys): if input_dict[key] == order: ordered_keys.append(key) remaining_keys.remove(key) - + ordered_keys.extend(remaining_keys) - + return ordered_keys + ordered_map_layers = sort_keys_by_order(layertypes_dict, layer_type_groups.keys()) -def create_base_style(sources=MAP_SOURCES,name='Footway Categories'): +def create_base_style(sources=MAP_SOURCES, name="Footway Categories"): + + default_color = "steelblue" custom_layer_colors = { - 'stairways':'#8a7e2f', - 'main_footways':'#299077', - 'informal_footways':'#b0645a', - 'potential_footways':'#9569a4', + "stairways": "#8a7e2f", + "main_footways": "#299077", + "informal_footways": "#b0645a", + "potential_footways": "#9569a4", } custom_layer_dash_patterns = { - "crossings": [1,0.5], + "crossings": [1, 0.5], } - + + custom_legend_widths = {"kerbs": 8} + style_dict = deepcopy(mapstyle_basedict) - - style_dict['sources'] = sources - - style_dict['name'] = name - - style_dict['layers'].extend(deepcopy(immutable_layers)) - + + style_dict["sources"] = sources + + style_dict["name"] = name + + style_dict["layers"].extend(deepcopy(immutable_layers)) + + # declaring the legend: + style_legend = StandaloneLegend() + for layername in ordered_map_layers: layer_type = layertypes_dict[layername] - + layer_dict = deepcopy(layertypes_basedict[layer_type]) - + # now we can set the id and source: - layer_dict['id'] = layername - layer_dict['source'] = f'oswm_pmtiles_{layername}' - layer_dict['source-layer'] = layername + layer_dict["id"] = layername + layer_dict["source"] = f"oswm_pmtiles_{layername}" + layer_dict["source-layer"] = layername if layername in custom_layer_colors: - layer_dict['paint']['line-color'] = custom_layer_colors[layername] - + layer_dict["paint"]["line-color"] = custom_layer_colors[layername] + if layername in custom_layer_dash_patterns: - layer_dict['paint']['line-dasharray'] = custom_layer_dash_patterns[layername] + layer_dict["paint"]["line-dasharray"] = custom_layer_dash_patterns[ + layername + ] # now the custom colors - - style_dict['layers'].append(layer_dict) - + style_dict["layers"].append(layer_dict) + + # adding to the legend: + style_legend.add_element( + layer_type, + layername, + **{ + "color": custom_layer_colors.get(layername, default_color), + "width": custom_legend_widths.get(layername, 4), + }, + ) + + style_legend.export( + os.path.join(map_symbols_assets_path, "footway_categories" + ".png") + ) + return style_dict -def create_simple_map_style(name,color_schema,color_dict,filename,sources=MAP_SOURCES,generate_shadow_layers=False): + +def create_simple_map_style( + name, + color_schema, + color_dict, + filename, + sources=MAP_SOURCES, + generate_shadow_layers=False, +): style_dict = deepcopy(mapstyle_basedict) - - style_dict['sources'] = sources - - style_dict['name'] = name - - style_dict['layers'].extend(deepcopy(immutable_layers)) - + + style_dict["sources"] = sources + + style_dict["name"] = name + + style_dict["layers"].extend(deepcopy(immutable_layers)) + # creating "shadow layers" for line layers only: if generate_shadow_layers: for layername in ordered_map_layers: - if layertypes_dict[layername] == 'line': - layer_dict = deepcopy(layertypes_basedict['line']) - layer_dict['id'] = f'{layername}_shadow' - layer_dict['source'] = f'oswm_pmtiles_{layername}' - layer_dict['source-layer'] = layername - layer_dict['paint']['line-color'] = 'black' - layer_dict['paint']['line-width'] = 4 - - style_dict['layers'].append(layer_dict) - + if layertypes_dict[layername] == "line": + layer_dict = deepcopy(layertypes_basedict["line"]) + layer_dict["id"] = f"{layername}_shadow" + layer_dict["source"] = f"oswm_pmtiles_{layername}" + layer_dict["source-layer"] = layername + layer_dict["paint"]["line-color"] = "black" + layer_dict["paint"]["line-width"] = 4 + + style_dict["layers"].append(layer_dict) + for layername in ordered_map_layers: layer_type = layertypes_dict[layername] - + layer_dict = deepcopy(layertypes_basedict[layer_type]) - + # now we can set the id and source: - layer_dict['id'] = layername - layer_dict['source'] = f'oswm_pmtiles_{layername}' - layer_dict['source-layer'] = layername - + layer_dict["id"] = layername + layer_dict["source"] = f"oswm_pmtiles_{layername}" + layer_dict["source-layer"] = layername + # layer_type = layertypes_dict[layername] - - layer_dict['paint'][color_attribute[layer_type]] = color_schema - - - style_dict['layers'].append(layer_dict) + + layer_dict["paint"][color_attribute[layer_type]] = color_schema + + style_dict["layers"].append(layer_dict) # now generating the map symbols # TODO: check the hashing, otherwise no need to re-run style_legend = StandaloneLegend() + custom_line_args = { + "linewidth": 4, + } + for key in color_dict: - style_legend.add_line(label=key, color=color_dict[key]) + style_legend.add_line(label=key, color=color_dict[key], **custom_line_args) - style_legend.add_line(label='other', color=color_schema[-1]) + style_legend.add_line(label="other", color=color_schema[-1], **custom_line_args) + + style_legend.export(os.path.join(map_symbols_assets_path, f"{filename}.png")) - style_legend.export(os.path.join(map_symbols_assets_path,f'{filename}.png')) - return style_dict -def get_color_dict(columnname,layer='sidewalks',attribute='color'): + +def get_color_dict(columnname, layer="sidewalks", attribute="color"): """ Given a columnname, layername and attribute, returns a dictionary mapping each value in the column to its corresponding attribute value. - + :param columnname: column name :param layer: layer name (default to 'sidewalks') :param attribute: attribute name (default to 'color') - + :return: a dictionary mapping each value in the column to its corresponding attribute value """ colordict = {} - + base_dict = fields_values_properties[layer][columnname] - + for key in base_dict: colordict[key] = base_dict[key][attribute] - + return colordict -def create_maplibre_color_schema(attribute_dict,attribute_name, else_color="gray"): + +def create_maplibre_color_schema(attribute_dict, attribute_name, else_color="gray"): schema = ["case"] for key, value in attribute_dict.items(): - schema.extend([ - ["==", ["get", attribute_name], key], - value - ]) + schema.extend([["==", ["get", attribute_name], key], value]) schema.append(else_color) return schema -def create_crossings_kerbs_style(sources=MAP_SOURCES,name='Crossings and Kerbs',else_color='#63636380'): + +def create_crossings_kerbs_style( + sources=MAP_SOURCES, name="Crossings and Kerbs", else_color="#63636380" +): style_dict = deepcopy(mapstyle_basedict) - - style_dict['sources'] = sources - - style_dict['name'] = name - - style_dict['layers'].extend(deepcopy(immutable_layers)) + + style_dict["sources"] = sources + + style_dict["name"] = name + + style_dict["layers"].extend(deepcopy(immutable_layers)) interest_layers = { # layername : tag key - 'crossings' : 'crossing', - 'kerbs' : 'kerb', + "crossings": "crossing", + "kerbs": "kerb", } for layername in ordered_map_layers: layer_type = layertypes_dict[layername] - + layer_dict = deepcopy(layertypes_basedict[layer_type]) if layername in interest_layers: # layer_dict.update(custom_crossing_kerbs_dict[layername]) - color_dict = get_color_dict(interest_layers[layername],layername) - color_schema = create_maplibre_color_schema(color_dict,interest_layers[layername],'gray') - layer_dict['paint'][color_attribute[layer_type]] = color_schema + color_dict = get_color_dict(interest_layers[layername], layername) + color_schema = create_maplibre_color_schema( + color_dict, interest_layers[layername], "gray" + ) + layer_dict["paint"][color_attribute[layer_type]] = color_schema # making kerbs a little bigger - if layername == 'kerbs': - layer_dict['paint']['circle-radius'] = [ - 'case', - ['boolean', ['feature-state', 'hover'], False], + if layername == "kerbs": + layer_dict["paint"]["circle-radius"] = [ + "case", + ["boolean", ["feature-state", "hover"], False], 8, - 5 + 5, ] else: # all other layers will be a very faded gray: - layer_dict['paint'][color_attribute[layer_type]] = else_color + layer_dict["paint"][color_attribute[layer_type]] = else_color # now we can set the id and source: - layer_dict['id'] = layername - layer_dict['source'] = f'oswm_pmtiles_{layername}' - layer_dict['source-layer'] = layername - - style_dict['layers'].append(layer_dict) + layer_dict["id"] = layername + layer_dict["source"] = f"oswm_pmtiles_{layername}" + layer_dict["source-layer"] = layername + style_dict["layers"].append(layer_dict) return style_dict + # call just once: create_folderlist([map_symbols_assets_path])