From 37e85060f0bbf92be3f1b340fb082fa3b4781e4a Mon Sep 17 00:00:00 2001 From: Nitin Magima Date: Mon, 13 Oct 2025 15:34:20 -0400 Subject: [PATCH 01/11] Update Madagascar configuration to use shapefile-based shapes --- fbfmaproom/fbfmaproom-sample.yaml | 73 +++++++++++++++---------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/fbfmaproom/fbfmaproom-sample.yaml b/fbfmaproom/fbfmaproom-sample.yaml index 7b998b18..80c7dcf3 100644 --- a/fbfmaproom/fbfmaproom-sample.yaml +++ b/fbfmaproom/fbfmaproom-sample.yaml @@ -140,22 +140,22 @@ countries: clip: no shapes: - name: National - sql: select adm0_code as key, adm0_name as label, ST_AsBinary(ST_Union(the_geom)) as the_geom - from iridb.g2015_2014_1 g where adm0_name = 'Madagascar' - and adm1_name in ('Androy', 'Anosy', 'Atsimo Andrefana', 'Atsimo Atsinanana') - group by 1, 2 + file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + layer: mdg_bnd_adm2_dis_pam + key_field: ADM0_PCODE + label_field: ADM0_EN - name: Regional - sql: select (adm0_code, adm1_code) as key, adm1_name as label, - ST_AsBinary(the_geom) as the_geom from iridb.g2015_2014_1 - where adm0_name = 'Madagascar' - and adm1_name in ('Androy', 'Anosy', 'Atsimo Andrefana', 'Atsimo Atsinanana') + file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + layer: mdg_bnd_adm2_dis_pam + key_field: ADM1_PCODE + label_field: ADM1_EN - name: District - sql: select (adm0_code, adm1_code, adm2_code) as key, adm2_name as label, - ST_AsBinary(the_geom) as the_geom from iridb.g2015_2014_2 - where adm0_name = 'Madagascar' - and adm1_name in ('Androy', 'Anosy', 'Atsimo Andrefana', 'Atsimo Atsinanana') + file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + layer: mdg_bnd_adm2_dis_pam + key_field: ADM2_PCODE + label_field: ADM2_EN datasets: defaults: @@ -382,22 +382,22 @@ countries: clip: no shapes: - name: National - sql: select adm0_code as key, adm0_name as label, ST_AsBinary(ST_Union(the_geom)) as the_geom - from iridb.g2015_2014_1 g where adm0_name = 'Madagascar' - and adm1_name in ('Androy', 'Anosy', 'Atsimo Andrefana', 'Atsimo Atsinanana') - group by 1, 2 + file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + layer: mdg_bnd_adm2_dis_pam + key_field: ADM0_PCODE + label_field: ADM0_EN - name: Regional - sql: select (adm0_code, adm1_code) as key, adm1_name as label, - ST_AsBinary(the_geom) as the_geom from iridb.g2015_2014_1 - where adm0_name = 'Madagascar' - and adm1_name in ('Androy', 'Anosy', 'Atsimo Andrefana', 'Atsimo Atsinanana') + file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + layer: mdg_bnd_adm2_dis_pam + key_field: ADM1_PCODE + label_field: ADM1_EN - name: District - sql: select (adm0_code, adm1_code, adm2_code) as key, adm2_name as label, - ST_AsBinary(the_geom) as the_geom from iridb.g2015_2014_2 - where adm0_name = 'Madagascar' - and adm1_name in ('Androy', 'Anosy', 'Atsimo Andrefana', 'Atsimo Atsinanana') + file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + layer: mdg_bnd_adm2_dis_pam + key_field: ADM2_PCODE + label_field: ADM2_EN datasets: defaults: @@ -577,23 +577,22 @@ countries: clip: no shapes: - name: National - sql: select adm0_code as key, adm0_name as label, ST_AsBinary(ST_Union(the_geom)) as the_geom - from iridb.g2015_2014_1 g where adm0_name = 'Madagascar' - and adm1_name in ('Androy', 'Anosy', 'Atsimo Andrefana', 'Atsimo Atsinanana') - group by 1, 2 - + file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + layer: mdg_bnd_adm2_dis_pam + key_field: ADM0_PCODE + label_field: ADM0_EN - name: Regional - sql: select (adm0_code, adm1_code) as key, adm1_name as label, - ST_AsBinary(the_geom) as the_geom from iridb.g2015_2014_1 - where adm0_name = 'Madagascar' - and adm1_name in ('Androy', 'Anosy', 'Atsimo Andrefana', 'Atsimo Atsinanana') + file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + layer: mdg_bnd_adm2_dis_pam + key_field: ADM1_PCODE + label_field: ADM1_EN - name: District - sql: select (adm0_code, adm1_code, adm2_code) as key, adm2_name as label, - ST_AsBinary(the_geom) as the_geom from iridb.g2015_2014_2 - where adm0_name = 'Madagascar' - and adm1_name in ('Androy', 'Anosy', 'Atsimo Andrefana', 'Atsimo Atsinanana') + file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + layer: mdg_bnd_adm2_dis_pam + key_field: ADM2_PCODE + label_field: ADM2_EN datasets: defaults: From 201335c75a78184c208bc9b68da2e1dcd5129d7e Mon Sep 17 00:00:00 2001 From: Nitin Magima Date: Mon, 27 Oct 2025 18:36:16 -0400 Subject: [PATCH 02/11] Update fbfmaproom-sample.yaml --- fbfmaproom/fbfmaproom-sample.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fbfmaproom/fbfmaproom-sample.yaml b/fbfmaproom/fbfmaproom-sample.yaml index 80c7dcf3..2e1f920b 100644 --- a/fbfmaproom/fbfmaproom-sample.yaml +++ b/fbfmaproom/fbfmaproom-sample.yaml @@ -141,19 +141,19 @@ countries: shapes: - name: National file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip - layer: mdg_bnd_adm2_dis_pam + layer: mdg_adm0 key_field: ADM0_PCODE label_field: ADM0_EN - name: Regional file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip - layer: mdg_bnd_adm2_dis_pam + layer: mdg_adm1 key_field: ADM1_PCODE label_field: ADM1_EN - name: District file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip - layer: mdg_bnd_adm2_dis_pam + layer: mdg_adm2 key_field: ADM2_PCODE label_field: ADM2_EN @@ -383,19 +383,19 @@ countries: shapes: - name: National file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip - layer: mdg_bnd_adm2_dis_pam + layer: mdg_adm0 key_field: ADM0_PCODE label_field: ADM0_EN - name: Regional file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip - layer: mdg_bnd_adm2_dis_pam + layer: mdg_adm1 key_field: ADM1_PCODE label_field: ADM1_EN - name: District file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip - layer: mdg_bnd_adm2_dis_pam + layer: mdg_adm2 key_field: ADM2_PCODE label_field: ADM2_EN @@ -578,19 +578,19 @@ countries: shapes: - name: National file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip - layer: mdg_bnd_adm2_dis_pam + layer: mdg_adm0 key_field: ADM0_PCODE label_field: ADM0_EN - name: Regional file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip - layer: mdg_bnd_adm2_dis_pam + layer: mdg_adm1 key_field: ADM1_PCODE label_field: ADM1_EN - name: District file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip - layer: mdg_bnd_adm2_dis_pam + layer: mdg_adm2 key_field: ADM2_PCODE label_field: ADM2_EN From 04087ed101dea94767f6b0a078b36cf5e67bc2a3 Mon Sep 17 00:00:00 2001 From: Aaron Kaplan Date: Thu, 30 Oct 2025 14:48:44 -0400 Subject: [PATCH 03/11] Accept shapes in zip or unpacked form --- fbfmaproom/fbfmaproom-sample.yaml | 6 +++--- fbfmaproom/fbfmaproom.py | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/fbfmaproom/fbfmaproom-sample.yaml b/fbfmaproom/fbfmaproom-sample.yaml index 2e1f920b..b8d9f976 100644 --- a/fbfmaproom/fbfmaproom-sample.yaml +++ b/fbfmaproom/fbfmaproom-sample.yaml @@ -140,19 +140,19 @@ countries: clip: no shapes: - name: National - file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + file: madagascar/shapes/2025-10/mdg_adm0.shp layer: mdg_adm0 key_field: ADM0_PCODE label_field: ADM0_EN - name: Regional - file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + file: madagascar/shapes/2025-10/mdg_adm1.shp layer: mdg_adm1 key_field: ADM1_PCODE label_field: ADM1_EN - name: District - file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + file: madagascar/shapes/2025-10/mdg_adm2.shp layer: mdg_adm2 key_field: ADM2_PCODE label_field: ADM2_EN diff --git a/fbfmaproom/fbfmaproom.py b/fbfmaproom/fbfmaproom.py index c344019b..f078faff 100644 --- a/fbfmaproom/fbfmaproom.py +++ b/fbfmaproom/fbfmaproom.py @@ -344,9 +344,13 @@ def retrieve_shapes( df = pd.read_sql(query, conn, params={"key": key}) if "the_geom" in fields: df["the_geom"] = df["the_geom"].apply(lambda x: wkb.loads(x.tobytes())) - elif 'file' in sc: - filepath = Path(CONFIG["data_root"]) / sc["file"] - with fiona.open(f"zip://{filepath}", layer=sc["layer"]) as collection: + elif "file" in sc: + if sc["file"].endswith(".zip"): + prefix = "zip://" + else: + prefix = "" + file_arg = f"{prefix}{CONFIG['data_root']}/{sc['file']}" + with fiona.open(file_arg, layer=sc["layer"]) as collection: tuples = [ ( feature['properties'][sc["key_field"]], From e9e28fa901c3433dc8ec480fe679236353687f3f Mon Sep 17 00:00:00 2001 From: Aaron Kaplan Date: Thu, 30 Oct 2025 14:49:15 -0400 Subject: [PATCH 04/11] Don't suppress error message when selected region isn't found --- fbfmaproom/fbfmaproom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fbfmaproom/fbfmaproom.py b/fbfmaproom/fbfmaproom.py index f078faff..9ecab295 100644 --- a/fbfmaproom/fbfmaproom.py +++ b/fbfmaproom/fbfmaproom.py @@ -1142,7 +1142,7 @@ def table_cb(issue_month_abbrev, freq, mode, geom_key, pathname, severity, predi try: if geom_key is None: - raise NotFoundError("No region found") + raise Exception("No region found") main_df, summary_df, thresholds = generate_tables( country_key, From 264ca86f46f377f82ad2f5fc8c746dbe4c9aaee1 Mon Sep 17 00:00:00 2001 From: Aaron Kaplan Date: Mon, 10 Nov 2025 19:28:50 -0500 Subject: [PATCH 05/11] Rename shapefile --- fbfmaproom/fbfmaproom-sample.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fbfmaproom/fbfmaproom-sample.yaml b/fbfmaproom/fbfmaproom-sample.yaml index b8d9f976..f234ebb1 100644 --- a/fbfmaproom/fbfmaproom-sample.yaml +++ b/fbfmaproom/fbfmaproom-sample.yaml @@ -140,19 +140,19 @@ countries: clip: no shapes: - name: National - file: madagascar/shapes/2025-10/mdg_adm0.shp + file: madagascar/shapes/2025-10/SHP_MERGED.zip layer: mdg_adm0 key_field: ADM0_PCODE label_field: ADM0_EN - name: Regional - file: madagascar/shapes/2025-10/mdg_adm1.shp + file: madagascar/shapes/2025-10/SHP_MERGED.zip layer: mdg_adm1 key_field: ADM1_PCODE label_field: ADM1_EN - name: District - file: madagascar/shapes/2025-10/mdg_adm2.shp + file: madagascar/shapes/2025-10/SHP_MERGED.zip layer: mdg_adm2 key_field: ADM2_PCODE label_field: ADM2_EN @@ -382,19 +382,19 @@ countries: clip: no shapes: - name: National - file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + file: madagascar/shapes/2025-10/SHP_MERGED.zip layer: mdg_adm0 key_field: ADM0_PCODE label_field: ADM0_EN - name: Regional - file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + file: madagascar/shapes/2025-10/SHP_MERGED.zip layer: mdg_adm1 key_field: ADM1_PCODE label_field: ADM1_EN - name: District - file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + file: madagascar/shapes/2025-10/SHP_MERGED.zip layer: mdg_adm2 key_field: ADM2_PCODE label_field: ADM2_EN @@ -577,19 +577,19 @@ countries: clip: no shapes: - name: National - file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + file: madagascar/shapes/2025-10/SHP_MERGED.zip layer: mdg_adm0 key_field: ADM0_PCODE label_field: ADM0_EN - name: Regional - file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + file: madagascar/shapes/2025-10/SHP_MERGED.zip layer: mdg_adm1 key_field: ADM1_PCODE label_field: ADM1_EN - name: District - file: madagascar/shapes/2025-10/SHP_ADM2_UPDATE.zip + file: madagascar/shapes/2025-10/SHP_MERGED.zip layer: mdg_adm2 key_field: ADM2_PCODE label_field: ADM2_EN From 683366fd26242951f3a4dc9495ccc509fc5d98de Mon Sep 17 00:00:00 2001 From: Aaron Kaplan Date: Mon, 10 Nov 2025 19:29:16 -0500 Subject: [PATCH 06/11] Re-enable clipping for Madagascar --- fbfmaproom/fbfmaproom-sample.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/fbfmaproom/fbfmaproom-sample.yaml b/fbfmaproom/fbfmaproom-sample.yaml index f234ebb1..05bd3444 100644 --- a/fbfmaproom/fbfmaproom-sample.yaml +++ b/fbfmaproom/fbfmaproom-sample.yaml @@ -137,7 +137,6 @@ countries: zoom: 7 rotation: 0 marker: [45.5, -24] - clip: no shapes: - name: National file: madagascar/shapes/2025-10/SHP_MERGED.zip @@ -379,7 +378,6 @@ countries: zoom: 7 rotation: 0 marker: [45.5, -24] - clip: no shapes: - name: National file: madagascar/shapes/2025-10/SHP_MERGED.zip @@ -574,7 +572,6 @@ countries: zoom: 7 rotation: 0 marker: [45.5, -24] - clip: no shapes: - name: National file: madagascar/shapes/2025-10/SHP_MERGED.zip From 5c33f4059370b75100096effc9a2f92f235fb09a Mon Sep 17 00:00:00 2001 From: Aaron Kaplan Date: Mon, 10 Nov 2025 19:37:26 -0500 Subject: [PATCH 07/11] Nitin's Madagascar shapes script --- .../madagascar/create_admin_levels_2025_10.py | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py diff --git a/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py b/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py new file mode 100644 index 00000000..ba044383 --- /dev/null +++ b/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +""" +Create Admin 0, Admin 1, and Admin 2 Shapefiles from Admin 2 Input + +This script processes Madagascar administrative boundary data to create +four separate shapefiles for different administrative levels: +- Admin 0: National boundaries (country level) +- Admin 1: Regional boundaries (province/region level) +- Admin 2: District boundaries (district level) +- ALL: Combined shapefile with all admin levels + +The script: +1. Dissolves (merges) lower-level boundaries to create higher-level boundaries +2. Reprojects from projected coordinate system (EPSG:3395 - meters) to + geographic coordinate system (EPSG:4326 - degrees) for web mapping compatibility +3. Maintains hierarchical field structure (e.g., Admin 1 includes Admin 0 fields) +4. Creates an ALL levels shapefile with admLevel field for filtering + +Requirements: + - geopandas + - pandas + - zipfile (standard library) + - tempfile (standard library) + - pathlib (standard library) + +Usage: + python create_admin_levels.py + +Input: + SHP_ADM2_UPDATE_dante.zip - Original admin 2 shapefile in projected CRS (EPSG:3395) + +Output: + SHP_ADM2_UPDATE.zip - New zip file containing all four admin level shapefiles + in geographic coordinate system (EPSG:4326) for Python maproom integration +""" + +import geopandas as gpd +import pandas as pd +import zipfile +import tempfile +import os +import shutil +from pathlib import Path + +def main(): + """ + Main function to process shapefiles and create admin levels. + + Process: + 1. Extract the original admin 2 shapefile from the input zip + 2. Read the shapefile using GeoPandas + 3. Reproject from EPSG:3395 (projected, meters) to EPSG:4326 (geographic, degrees) + 4. Create admin 0 by dissolving all polygons into one national boundary + 5. Create admin 1 by dissolving polygons grouped by region code (includes Admin 0 fields) + 6. Create admin 2 by filtering columns from the original data (includes Admin 0 + Admin 1 fields) + 7. Create ALL levels shapefile by combining all admin levels with admLevel field + 8. Save all four shapefiles with consistent naming + 9. Package everything into a new zip file with geographic coordinates + """ + # Input and output paths + input_zip = "SHP_ADM2_UPDATE_dante.zip" + output_zip = "SHP_ADM2_UPDATE.zip" + + # Create temporary directory for processing + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + + print("Extracting shapefile from zip...") + # Extract the shapefile from zip + with zipfile.ZipFile(input_zip, 'r') as zip_ref: + zip_ref.extractall(temp_path) + + # Read the admin 2 shapefile + shapefile_path = temp_path / "SHP_ADM2_UPDATE" / "mdg_bnd_adm2_dis_pam.shp" + print(f"Reading shapefile: {shapefile_path}") + gdf = gpd.read_file(shapefile_path) + + print(f"Original shapefile has {len(gdf)} features") + print(f"Columns: {list(gdf.columns)}") + print(f"Original CRS: {gdf.crs}") + + # Reproject from projected coordinate system to geographic coordinate system + # Input: EPSG:3395 (WGS 84 / World Mercator - projected, units in meters) + # Output: EPSG:4326 (WGS 84 - geographic, units in degrees) + # This conversion is required for Python maproom applications which expect + # geographic coordinates in degrees (lat/lon) rather than projected meters + print(f"\nReprojecting from {gdf.crs} to EPSG:4326 (WGS 84 - geographic)...") + gdf = gdf.to_crs('EPSG:4326') + print(f"Reprojected CRS: {gdf.crs}") + print("Coordinates are now in degrees (longitude, latitude)") + + # Create admin 0 (National) - dissolve by ADM0_PCODE + # This merges all 120 district polygons into 1 national boundary + print("\nCreating Admin 0 (National) shapefile...") + adm0 = gdf.dissolve(by='ADM0_PCODE', aggfunc='first') + # Reset index to make ADM0_PCODE a column again, then select fields + adm0 = adm0.reset_index() + adm0 = adm0[['ADM0_PCODE', 'ADM0_EN', 'geometry']] + print(f"Admin 0 has {len(adm0)} feature(s)") + + # Create admin 1 (Regional) - dissolve by ADM1_PCODE + # This merges district polygons that share the same region code + # Include hierarchical fields (Admin 0 + Admin 1 fields) + print("\nCreating Admin 1 (Regional) shapefile...") + adm1 = gdf.dissolve(by='ADM1_PCODE', aggfunc='first') + # Reset index to make ADM1_PCODE a column again, then select fields + adm1 = adm1.reset_index() + adm1 = adm1[['ADM0_PCODE', 'ADM0_EN', 'ADM1_PCODE', 'ADM1_EN', 'ADM1_TYPE', 'geometry']] + print(f"Admin 1 has {len(adm1)} feature(s)") + + # Create admin 2 (District) - keep original geometries but filter columns + # This preserves all 120 district boundaries + # Include hierarchical fields (Admin 0 + Admin 1 + Admin 2 fields) + print("\nCreating Admin 2 (District) shapefile...") + adm2 = gdf[['ADM0_PCODE', 'ADM0_EN', 'ADM1_PCODE', 'ADM1_EN', 'ADM1_TYPE', 'ADM2_PCODE', 'ADM2_EN', 'ADM2_TYPE', 'geometry']].copy() + print(f"Admin 2 has {len(adm2)} feature(s)") + + # Create ALL levels shapefile - combine all admin levels with admLevel field + # This creates a single shapefile containing all administrative levels, + # distinguished by the admLevel field (0=National, 1=Regional, 2=District) + # This format matches the structure used in reference datasets (e.g., Djibouti) + print("\nCreating ALL levels shapefile...") + # Add admLevel field to each dataframe to distinguish admin levels + adm0_all = adm0.copy() + adm0_all['admLevel'] = 0 # National level + + adm1_all = adm1.copy() + adm1_all['admLevel'] = 1 # Regional level + + adm2_all = adm2.copy() + adm2_all['admLevel'] = 2 # District level + + # Combine all levels into a single GeoDataFrame + all_levels = gpd.GeoDataFrame(pd.concat([adm0_all, adm1_all, adm2_all], ignore_index=True)) + print(f"ALL levels has {len(all_levels)} feature(s) (1 National + {len(adm1_all)} Regional + {len(adm2_all)} District)") + + # Save shapefiles to temporary directory + print("\nSaving shapefiles...") + adm0_path = temp_path / "mdg_adm0.shp" + adm1_path = temp_path / "mdg_adm1.shp" + adm2_path = temp_path / "mdg_adm2.shp" + all_path = temp_path / "mdg_adm_ALL.shp" + + adm0.to_file(adm0_path) + adm1.to_file(adm1_path) + adm2.to_file(adm2_path) + all_levels.to_file(all_path) + + print(f"Saved Admin 0: {adm0_path}") + print(f"Saved Admin 1: {adm1_path}") + print(f"Saved Admin 2: {adm2_path}") + print(f"Saved ALL levels: {all_path}") + + # Create new zip file with all four shapefiles + print(f"\nCreating new zip file: {output_zip}") + with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zip_ref: + # Add all shapefile components for each admin level + for admin_level in ['mdg_adm0', 'mdg_adm1', 'mdg_adm2', 'mdg_adm_ALL']: + for ext in ['.shp', '.shx', '.dbf', '.prj', '.cpg', '.sbn', '.sbx', '.shp.xml']: + file_path = temp_path / f"{admin_level}{ext}" + if file_path.exists(): + zip_ref.write(file_path, file_path.name) + print(f" Added: {file_path.name}") + + print(f"\nSuccessfully created {output_zip} with all admin levels!") + print("\nLayer names:") + print("- Admin 0 (National): mdg_adm0") + print("- Admin 1 (Regional): mdg_adm1") + print("- Admin 2 (District): mdg_adm2") + print("- ALL levels: mdg_adm_ALL") + print(f"\nAll shapefiles are in EPSG:4326 (WGS 84 - geographic coordinate system)") + print("Coordinates are in degrees (longitude, latitude) for web mapping compatibility") + +if __name__ == "__main__": + main() From 1e64f6216160f65776673d931a1f1ef45b3a714d Mon Sep 17 00:00:00 2001 From: Aaron Kaplan Date: Mon, 10 Nov 2025 19:38:51 -0500 Subject: [PATCH 08/11] Remove holes caused by neighboring regions whose edges don't exactly match up --- .../madagascar/create_admin_levels_2025_10.py | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py b/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py index ba044383..60b5e122 100644 --- a/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py +++ b/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py @@ -41,6 +41,7 @@ import os import shutil from pathlib import Path +import shapely def main(): """ @@ -92,7 +93,7 @@ def main(): # Create admin 0 (National) - dissolve by ADM0_PCODE # This merges all 120 district polygons into 1 national boundary print("\nCreating Admin 0 (National) shapefile...") - adm0 = gdf.dissolve(by='ADM0_PCODE', aggfunc='first') + adm0 = merge(gdf, 'ADM0_PCODE') # Reset index to make ADM0_PCODE a column again, then select fields adm0 = adm0.reset_index() adm0 = adm0[['ADM0_PCODE', 'ADM0_EN', 'geometry']] @@ -102,7 +103,7 @@ def main(): # This merges district polygons that share the same region code # Include hierarchical fields (Admin 0 + Admin 1 fields) print("\nCreating Admin 1 (Regional) shapefile...") - adm1 = gdf.dissolve(by='ADM1_PCODE', aggfunc='first') + adm1 = merge(gdf, 'ADM1_PCODE') # Reset index to make ADM1_PCODE a column again, then select fields adm1 = adm1.reset_index() adm1 = adm1[['ADM0_PCODE', 'ADM0_EN', 'ADM1_PCODE', 'ADM1_EN', 'ADM1_TYPE', 'geometry']] @@ -171,5 +172,26 @@ def main(): print(f"\nAll shapefiles are in EPSG:4326 (WGS 84 - geographic coordinate system)") print("Coordinates are in degrees (longitude, latitude) for web mapping compatibility") + +def merge(gdf, by): + merged_df = gdf.dissolve(by=by) + merged_df['geometry'] = [ + remove_holes(geom) for geom in merged_df['geometry'] + ] + return merged_df + + +def remove_holes(geom): + if isinstance(geom, shapely.Polygon): + result = shapely.Polygon(geom.exterior) + elif isinstance(geom, shapely.MultiPolygon): + result = shapely.MultiPolygon([ + shapely.Polygon(poly.exterior) for poly in geom.geoms + ]) + else: + raise Exception(f"Bad argument type for remove_holes: {type(geom)}") + return result + + if __name__ == "__main__": main() From 1d33ab8929acd3405708aa02fab36860b5a20635 Mon Sep 17 00:00:00 2001 From: Aaron Kaplan Date: Mon, 10 Nov 2025 19:39:32 -0500 Subject: [PATCH 09/11] Change file names --- .../madagascar/create_admin_levels_2025_10.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py b/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py index 60b5e122..6dcccf83 100644 --- a/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py +++ b/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py @@ -27,10 +27,10 @@ python create_admin_levels.py Input: - SHP_ADM2_UPDATE_dante.zip - Original admin 2 shapefile in projected CRS (EPSG:3395) + SHP_ADM2_UPDATE.zip - Original admin 2 shapefile in projected CRS (EPSG:3395) Output: - SHP_ADM2_UPDATE.zip - New zip file containing all four admin level shapefiles + SHP_MERGED.zip - New zip file containing all three admin level shapefiles in geographic coordinate system (EPSG:4326) for Python maproom integration """ @@ -59,8 +59,8 @@ def main(): 9. Package everything into a new zip file with geographic coordinates """ # Input and output paths - input_zip = "SHP_ADM2_UPDATE_dante.zip" - output_zip = "SHP_ADM2_UPDATE.zip" + input_zip = "SHP_ADM2_UPDATE.zip" + output_zip = "SHP_MERGED.zip" # Create temporary directory for processing with tempfile.TemporaryDirectory() as temp_dir: From a29f0da4591f3f4d056ab903a19bd9c6b51c04ec Mon Sep 17 00:00:00 2001 From: Aaron Kaplan Date: Mon, 10 Nov 2025 19:40:19 -0500 Subject: [PATCH 10/11] Remove unnecessary ALL layer --- .../madagascar/create_admin_levels_2025_10.py | 27 ++----------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py b/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py index 6dcccf83..0da485ba 100644 --- a/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py +++ b/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py @@ -116,47 +116,25 @@ def main(): adm2 = gdf[['ADM0_PCODE', 'ADM0_EN', 'ADM1_PCODE', 'ADM1_EN', 'ADM1_TYPE', 'ADM2_PCODE', 'ADM2_EN', 'ADM2_TYPE', 'geometry']].copy() print(f"Admin 2 has {len(adm2)} feature(s)") - # Create ALL levels shapefile - combine all admin levels with admLevel field - # This creates a single shapefile containing all administrative levels, - # distinguished by the admLevel field (0=National, 1=Regional, 2=District) - # This format matches the structure used in reference datasets (e.g., Djibouti) - print("\nCreating ALL levels shapefile...") - # Add admLevel field to each dataframe to distinguish admin levels - adm0_all = adm0.copy() - adm0_all['admLevel'] = 0 # National level - - adm1_all = adm1.copy() - adm1_all['admLevel'] = 1 # Regional level - - adm2_all = adm2.copy() - adm2_all['admLevel'] = 2 # District level - - # Combine all levels into a single GeoDataFrame - all_levels = gpd.GeoDataFrame(pd.concat([adm0_all, adm1_all, adm2_all], ignore_index=True)) - print(f"ALL levels has {len(all_levels)} feature(s) (1 National + {len(adm1_all)} Regional + {len(adm2_all)} District)") - # Save shapefiles to temporary directory print("\nSaving shapefiles...") adm0_path = temp_path / "mdg_adm0.shp" adm1_path = temp_path / "mdg_adm1.shp" adm2_path = temp_path / "mdg_adm2.shp" - all_path = temp_path / "mdg_adm_ALL.shp" adm0.to_file(adm0_path) adm1.to_file(adm1_path) adm2.to_file(adm2_path) - all_levels.to_file(all_path) print(f"Saved Admin 0: {adm0_path}") print(f"Saved Admin 1: {adm1_path}") print(f"Saved Admin 2: {adm2_path}") - print(f"Saved ALL levels: {all_path}") - # Create new zip file with all four shapefiles + # Create new zip file with all three shapefiles print(f"\nCreating new zip file: {output_zip}") with zipfile.ZipFile(output_zip, 'w', zipfile.ZIP_DEFLATED) as zip_ref: # Add all shapefile components for each admin level - for admin_level in ['mdg_adm0', 'mdg_adm1', 'mdg_adm2', 'mdg_adm_ALL']: + for admin_level in ['mdg_adm0', 'mdg_adm1', 'mdg_adm2']: for ext in ['.shp', '.shx', '.dbf', '.prj', '.cpg', '.sbn', '.sbx', '.shp.xml']: file_path = temp_path / f"{admin_level}{ext}" if file_path.exists(): @@ -168,7 +146,6 @@ def main(): print("- Admin 0 (National): mdg_adm0") print("- Admin 1 (Regional): mdg_adm1") print("- Admin 2 (District): mdg_adm2") - print("- ALL levels: mdg_adm_ALL") print(f"\nAll shapefiles are in EPSG:4326 (WGS 84 - geographic coordinate system)") print("Coordinates are in degrees (longitude, latitude) for web mapping compatibility") From 034f155ba313aa47b5fcd0fca973969f80a07090 Mon Sep 17 00:00:00 2001 From: Aaron Kaplan Date: Mon, 10 Nov 2025 19:48:47 -0500 Subject: [PATCH 11/11] Freeze conda environment for mada shapes script --- .../shapes-2025-10/conda-packages.txt | 142 ++++++++++++++++++ .../create_admin_levels.py} | 0 2 files changed, 142 insertions(+) create mode 100644 fbfmaproom/data-conversion-scripts/madagascar/shapes-2025-10/conda-packages.txt rename fbfmaproom/data-conversion-scripts/madagascar/{create_admin_levels_2025_10.py => shapes-2025-10/create_admin_levels.py} (100%) diff --git a/fbfmaproom/data-conversion-scripts/madagascar/shapes-2025-10/conda-packages.txt b/fbfmaproom/data-conversion-scripts/madagascar/shapes-2025-10/conda-packages.txt new file mode 100644 index 00000000..47e343e3 --- /dev/null +++ b/fbfmaproom/data-conversion-scripts/madagascar/shapes-2025-10/conda-packages.txt @@ -0,0 +1,142 @@ +# This file may be used to create an environment using: +# $ conda create --name --file +# platform: linux-64 +# created-by: conda 24.9.2 +@EXPLICIT +https://repo.anaconda.com/pkgs/main/linux-64/_libgcc_mutex-0.1-main.conda +https://repo.anaconda.com/pkgs/main/linux-64/blas-1.0-mkl.conda +https://repo.anaconda.com/pkgs/main/linux-64/ca-certificates-2025.11.4-h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/intel-openmp-2025.0.0-h06a4308_1171.conda +https://repo.anaconda.com/pkgs/main/linux-64/ld_impl_linux-64-2.44-h153f514_2.conda +https://repo.anaconda.com/pkgs/main/linux-64/libgomp-15.2.0-h4751f2c_7.conda +https://repo.anaconda.com/pkgs/main/linux-64/python_abi-3.13-1_cp313.conda +https://repo.anaconda.com/pkgs/main/noarch/tzdata-2025b-h04d1e81_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/_openmp_mutex-5.1-1_gnu.conda +https://repo.anaconda.com/pkgs/main/linux-64/libgcc-15.2.0-h69a1729_7.conda +https://repo.anaconda.com/pkgs/main/linux-64/libgcc-ng-15.2.0-h166f726_7.conda +https://repo.anaconda.com/pkgs/main/linux-64/libgfortran5-15.2.0-hc633d37_7.conda +https://repo.anaconda.com/pkgs/main/linux-64/libstdcxx-15.2.0-h39759b7_7.conda +https://repo.anaconda.com/pkgs/main/linux-64/bzip2-1.0.8-h5eee18b_6.conda +https://repo.anaconda.com/pkgs/main/linux-64/dav1d-1.2.1-h5eee18b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/fribidi-1.0.10-h7b6447c_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/giflib-5.2.2-h5eee18b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/jansson-2.14-h5eee18b_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/jpeg-9e-h5eee18b_3.conda +https://repo.anaconda.com/pkgs/main/linux-64/json-c-0.16-h5eee18b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libdeflate-1.22-h5eee18b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libev-4.33-h7f8727e_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/libgfortran-15.2.0-h166f726_7.conda +https://repo.anaconda.com/pkgs/main/linux-64/libiconv-1.16-h5eee18b_3.conda +https://repo.anaconda.com/pkgs/main/linux-64/libmpdec-4.0.0-h5eee18b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libopenjpeg-2.5.4-hee96239_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/libstdcxx-ng-15.2.0-hc03a8fd_7.conda +https://repo.anaconda.com/pkgs/main/linux-64/libunistring-1.3-hb25bd0a_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libuuid-1.41.5-h5eee18b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libwebp-base-1.3.2-h5eee18b_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/libzlib-1.3.1-hb25bd0a_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/ncurses-6.5-h7934f7d_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/openssl-3.0.18-hd6dcaed_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/pthread-stubs-0.3-h0ce48e5_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/uriparser-0.9.8-h451ca9b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/xorg-libxau-1.0.12-h9b100fa_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/xorg-libxdmcp-1.1.5-h9b100fa_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/xorg-xorgproto-2024.1-h5eee18b_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/xz-5.6.4-h5eee18b_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/aom-3.6.0-h6a678d5_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/c-ares-1.34.5-hef5626c_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/expat-2.7.3-h3385a95_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/geos-3.10.6-h6a678d5_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/graphite2-1.3.14-h295c915_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/icu-73.1-h6a678d5_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/lerc-4.0.0-h6a678d5_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libffi-3.4.4-h6a678d5_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/libgfortran-ng-15.2.0-h166f726_7.conda +https://repo.anaconda.com/pkgs/main/linux-64/libxcb-1.17.0-h9b100fa_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/lz4-c-1.9.4-h6a678d5_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/pixman-0.46.4-h7934f7d_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/readline-8.3-hc2a1206_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/tbb-2022.0.0-hdb19cb5_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/zlib-1.3.1-hb25bd0a_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libavif-1.1.1-h5eee18b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libkml-1.3.0-h096b73e_7.conda +https://repo.anaconda.com/pkgs/main/linux-64/libpng-1.6.50-h2ed474d_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libssh2-1.11.1-h251f7ec_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libxml2-2.13.9-h2c43086_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/pcre2-10.42-hebb0a14_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/sqlite-3.51.0-h2a70700_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/tbb-devel-2022.0.0-hdb19cb5_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/xerces-c-3.2.4-h6a678d5_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/xorg-libx11-1.8.12-h9b100fa_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/zstd-1.5.7-h11fc155_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/blosc-1.21.6-hf7d4471_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/freetype-2.13.3-h4a9f257_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/gettext-0.21.0-hedfda30_2.conda +https://repo.anaconda.com/pkgs/main/linux-64/libarchive-3.7.7-hfab0078_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libglib-2.84.2-h37c7471_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libnghttp2-1.67.1-h697f920_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libtiff-4.7.0-hde9077f_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/minizip-4.0.3-hf59b114_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/mkl-2025.0.0-hacee8c2_941.conda +https://repo.anaconda.com/pkgs/main/linux-64/tk-8.6.15-h54e0aa7_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/xorg-libxext-1.3.6-h9b100fa_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/xorg-libxrender-0.9.12-h9b100fa_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/fontconfig-2.15.0-h2c49b7f_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/freexl-2.0.0-hf309648_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/lcms2-2.16-h92b89f2_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/libidn2-2.3.8-hf80d704_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/python-3.13.9-h7e8bc2b_100_cp313.conda +https://repo.anaconda.com/pkgs/main/linux-64/cairo-1.18.4-h44eff21_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/certifi-2025.10.5-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/charset-normalizer-3.4.4-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/noarch/cycler-0.11.0-pyhd3eb1b0_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/fonttools-4.60.1-py313hee96239_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/idna-3.11-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/joblib-1.5.2-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/kiwisolver-1.4.8-py313h6a678d5_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/libcurl-8.16.0-heebcbe5_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/markupsafe-3.0.2-py313h5eee18b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/mkl-service-2.5.2-py313hacdc0fc_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/networkx-3.5-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/packaging-25.0-py313h06a4308_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/pycparser-2.23-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/pyparsing-3.2.0-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/pysocks-1.7.1-py313h06a4308_1.conda +https://repo.anaconda.com/pkgs/main/noarch/python-tzdata-2025.2-pyhd3eb1b0_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/pytz-2025.2-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/setuptools-80.9.0-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/six-1.17.0-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/threadpoolctl-3.5.0-py313h7040dfc_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/wheel-0.45.1-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/xyzservices-2025.4.0-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/cffi-2.0.0-py313h4eded50_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/harfbuzz-10.2.0-hdfddeaa_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/jinja2-3.1.6-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/numpy-base-2.3.4-py313h95072fd_1.conda +https://repo.anaconda.com/pkgs/main/noarch/pip-25.2-pyhc872135_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/proj-9.3.1-h4591001_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/python-dateutil-2.9.0post0-py313h06a4308_2.conda +https://repo.anaconda.com/pkgs/main/linux-64/branca-0.8.1-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/brotlicffi-1.0.9.2-py313hbdd6827_2.conda +https://repo.anaconda.com/pkgs/main/linux-64/geotiff-1.7.0-ha59944b_4.conda +https://repo.anaconda.com/pkgs/main/linux-64/libspatialite-5.1.0-h798b768_2.conda +https://repo.anaconda.com/pkgs/main/linux-64/pillow-12.0.0-py313h3b88751_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/pyproj-3.6.1-py313h75c29da_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/libgdal-core-3.11.0-h177e9c5_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/urllib3-2.5.0-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/requests-2.32.5-py313h06a4308_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/bottleneck-1.4.2-py313haa0f9ac_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/contourpy-1.3.1-py313hdb19cb5_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/folium-0.20.0-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/matplotlib-base-3.10.6-py313h4948728_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/mkl_fft-2.1.1-py313h57662e1_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/mkl_random-1.3.0-py313h23c847b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/numpy-2.3.4-py313h720eef7_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/numexpr-2.14.1-py313h41d4191_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/pyogrio-0.10.0-py313h16bd0e6_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/scipy-1.16.3-py313h33bc11c_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/shapely-2.1.1-py313h05a33e2_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/pandas-2.3.3-py313h23c847b_1.conda +https://repo.anaconda.com/pkgs/main/linux-64/scikit-learn-1.7.2-py313h23c847b_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/geopandas-base-1.1.1-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/mapclassify-2.10.0-py313h06a4308_0.conda +https://repo.anaconda.com/pkgs/main/linux-64/geopandas-1.1.1-py313h06a4308_0.conda diff --git a/fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py b/fbfmaproom/data-conversion-scripts/madagascar/shapes-2025-10/create_admin_levels.py similarity index 100% rename from fbfmaproom/data-conversion-scripts/madagascar/create_admin_levels_2025_10.py rename to fbfmaproom/data-conversion-scripts/madagascar/shapes-2025-10/create_admin_levels.py