Skip to content
Open
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# This file may be used to create an environment using:
# $ conda create --name <env> --file <this 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#!/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.zip - Original admin 2 shapefile in projected CRS (EPSG:3395)

Output:
SHP_MERGED.zip - New zip file containing all three 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
import shapely

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.zip"
output_zip = "SHP_MERGED.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 = 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']]
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 = 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']]
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)")

# 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"

adm0.to_file(adm0_path)
adm1.to_file(adm1_path)
adm2.to_file(adm2_path)

print(f"Saved Admin 0: {adm0_path}")
print(f"Saved Admin 1: {adm1_path}")
print(f"Saved Admin 2: {adm2_path}")

# 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']:
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(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()
Loading