diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b799a7f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +*.png +*json diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bb394fd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM ubuntu:22.04 + +ARG PLZ_DATA=Postleitzahlengebiete_-_OSM.geojson +ARG PLZ_HIGHLIGHT=wtf_member_plz_prefixes.json + +# Set up environment with dependencies +RUN apt-get update && apt-get install libblas-dev python3-pip libgeos-dev wget -y +COPY requirements.txt ./ +RUN pip install -r requirements.txt + +# Copy all relevant data +COPY $PLZ_DATA ./ +COPY $PLZ_HIGHLIGHT ./ + +# Render map +COPY main.py ./ +RUN python3 main.py --plz-data $PLZ_DATA --plz-highlight $PLZ_HIGHLIGHT --out /tmp/map.png + +# Output map; use with "docker build -t wtf-map . && docker run wtf-map > map.png" +CMD ["cat", "/tmp/map.png"] diff --git a/README.md b/README.md index 1e2916f..5691b4e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,35 @@ -# member-map +# Map of Germany with highlighted WTF member locations -Generate a map of Germany with highlighted WTF member locations. \ No newline at end of file +# Docker Usage + +Download the Germany PLZ dataset in the GeoJSON format from +https://opendata-esri-de.opendata.arcgis.com/datasets/5b203df4357844c8a6715d7d411a8341_0 +and place the file in the root of this repository. + +Place a JSON file containing a list of PLZ prefixes as `wtf_member_plz_prefixes.json` in +the root of this repository. +The file contents should look like `["121", "635"]` for PLZ data that is anonymized to +the first three digits. + +The following will build a Docker image and render the map with the default +configuration for the WTF member PLZ prefix data: +``` +docker build -t wtf-map . +docker run wtf-map > map.png +``` + +Note, the build command already renders the map and thus can take a moment. +The run command just outputs the already rendered image to be piped into any target +file on the host system. + +## Script Usage + +In case you'd like custom colors or use different data, you are encouraged to use the +script explicity. +To get all relevant requirements, you can `pip install -r requirements.txt`, but note, +you might need to install BLAS and GEOS libraries on your system. + +The following will tell you all about what you can adjust for a custom visualization: +``` +python main.py --help +``` diff --git a/main.py b/main.py new file mode 100644 index 0000000..211a11b --- /dev/null +++ b/main.py @@ -0,0 +1,103 @@ +import argparse +import json + +import cartopy.crs as ccrs +import geopandas as gpd +import geoplot +import geoplot.crs as gcrs +import matplotlib.pyplot as plt + + +def main( + plz_data_path: str, + plz_prefixes_path: str, + output_file_path: str, + map_background_color: str, + map_accent_color: str, + image_background_color: str, +): + if not output_file_path.endswith(".png"): + raise ValueError(f"Output file needs to be a .png but is {output_file_path}") + + with open(plz_prefixes_path, "r") as fh: + plz_prefixes = json.load(fh) + with open(plz_data_path, "r") as fh: + plz_geojson_data = json.load(fh) + + data = gpd.GeoDataFrame.from_features(plz_geojson_data["features"]) + data["highlighted"] = False + for plz_prefix in plz_prefixes: + data.loc[data["plz"].str.startswith(plz_prefix), "highlighted"] = True + + ax = plt.axes(projection=ccrs.TransverseMercator()) + geoplot.polyplot( + data, + projection=gcrs.TransverseMercator(), + edgecolor=map_background_color, + facecolor=map_background_color, + linewidth=0.3, + ax=ax, + ) + geoplot.polyplot( + data[data["highlighted"]], + projection=gcrs.TransverseMercator(), + edgecolor=map_accent_color, + facecolor=map_accent_color, + linewidth=0.3, + ax=ax, + ) + + ax.set_facecolor(image_background_color) + plt.tight_layout() + plt.savefig( + output_file_path, dpi=300, facecolor=image_background_color, edgecolor="none" + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "--plz-data", + required=True, + help="A GEOJSON file with a 'plz' feature, like the one for Germany that can be" + + " downloaded from " + + "https://opendata-esri-de.opendata.arcgis.com/datasets/5b203df4357844c8a6715d7d411a8341_0", + ) + parser.add_argument( + "--plz-highlight", + required=True, + help="A JSON file with a list of PLZ (prefixes) that should be highlighted.", + ) + parser.add_argument( + "--out", + required=True, + help="A .png file path where the generated image should be stored.", + ) + parser.add_argument( + "--img-bg-color", + type=str, + default="#EDEFEB", + help="Color outside of the map, the image file background.", + ) + parser.add_argument( + "--map-bg-color", + type=str, + default="#202020", + help="Color code for the background map, non highlighted areas.", + ) + parser.add_argument( + "--map-accent-color", + type=str, + default="#EF7C21", + help="Color code for the highlighted areas on the map.", + ) + args = parser.parse_args() + + main( + plz_data_path=args.plz_data, + plz_prefixes_path=args.plz_highlight, + output_file_path=args.out, + map_background_color=args.map_bg_color, + map_accent_color=args.map_accent_color, + image_background_color=args.img_bg_color, + ) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ae69ef6 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +geopandas==0.11.1 +geoplot==0.5.1