Skip to content

South of France Restaurants

import geopandas as gp
import pandas as pd
import plotnine as gg

from ninejs import interactive, save, css


geo_url = "https://raw.githubusercontent.com/gregoiredavid/france-geojson/master/communes.geojson"
data_url = "https://raw.githubusercontent.com/holtzy/R-graph-gallery/master/DATA/data_on_french_states.csv"
departments = ["06", "83", "13", "30", "34", "11", "66"]
background_color = "#f5f5f2"
text_color = "#34322f"

communes = gp.read_file(geo_url)
communes = communes[communes["code"].str[:2].isin(departments)].copy()

restaurants = pd.read_csv(data_url, sep=";")
restaurants = restaurants.groupby("depcom", as_index=False)["nb_equip"].sum()

data = communes.merge(restaurants, how="left", left_on="code", right_on="depcom")
data["nb_equip"] = data["nb_equip"].fillna(0).astype(int)
data["fill_value"] = data["nb_equip"].clip(lower=0.1)
data["tooltip"] = [
    f"{name}: {value:,} restaurants" if value else f"{name}: no restaurants"
    for name, value in zip(data["nom"], data["nb_equip"])
]

data = data.to_crs(epsg=2154)
labels = data[
    data["nom"].isin(
        [
            "Nice",
            "Montpellier",
            "Cannes",
            "Aix-en-Provence",
            "Nîmes",
            "Toulon",
            "Perpignan",
        ]
    )
].copy()
points = labels.geometry.representative_point()
labels = labels.set_geometry(points).to_crs(epsg=4326)
labels["lon"] = labels.geometry.x
labels["lat"] = labels.geometry.y
label_positions = pd.DataFrame(
    {
        "nom": [
            "Nice",
            "Montpellier",
            "Cannes",
            "Aix-en-Provence",
            "Nîmes",
            "Toulon",
            "Perpignan",
        ],
        "label_x": [7.18, 3.35, 6.72, 5.10, 4.30, 6.15, 2.20],
        "label_y": [44.18, 44.00, 43.48, 43.70, 44.42, 42.98, 42.58],
    }
)
labels = labels.merge(label_positions, on="nom")
labels["label"] = labels.apply(
    lambda row: f"{row['nom']}\n{row['nb_equip']:,}",
    axis=1,
)

data["geometry"] = data.geometry.simplify(650, preserve_topology=True)
data = data.to_crs(epsg=4326)
caption = "Data: INSEE | Design: Yan Holtz | r-graph-gallery.com"

plot = (
    gg.ggplot(data)
    + gg.geom_map(
        gg.aes(fill="fill_value", tooltip="tooltip", data_id="code"),
        color="white",
        size=0.03,
        alpha=0.95,
    )
    + gg.geom_segment(
        data=labels,
        mapping=gg.aes(x="label_x", y="label_y", xend="lon", yend="lat"),
        inherit_aes=False,
        color=text_color,
        size=0.35,
    )
    + gg.geom_label(
        data=labels,
        mapping=gg.aes(x="label_x", y="label_y", label="label"),
        inherit_aes=False,
        fill=background_color,
        color=text_color,
        size=7.5,
        label_size=0,
        fontweight="bold",
    )
    + gg.annotate(
        "text",
        x=1.32,
        y=44.80,
        label="South of France\nrestaurant concentration",
        ha="left",
        va="top",
        size=18,
        fontweight="bold",
        color=text_color,
    )
    + gg.annotate(
        "text",
        x=1.32,
        y=44.32,
        label=(
            "Municipality totals, shown on a log color scale\n"
            "to keep small towns visible beside coastal hubs."
        ),
        ha="left",
        va="top",
        size=8.5,
        color=text_color,
    )
    + gg.annotate(
        "text",
        x=1.32,
        y=42.25,
        label=caption,
        ha="left",
        va="bottom",
        size=6.5,
        color=text_color,
    )
    + gg.scale_fill_cmap(
        "viridis",
        name="Restaurants",
        trans="log10",
        limits=(0.1, 2300),
        breaks=[0.1, 1, 10, 100, 1000],
        labels=["0", "1", "10", "100", "1,000"],
    )
    + gg.guides(fill=gg.guide_colorbar(direction="horizontal"))
    + gg.coord_cartesian(xlim=(1.2, 7.85), ylim=(42.15, 44.90), expand=False)
    + gg.theme_void(base_size=8)
    + gg.theme(
        figure_size=(10, 6),
        legend_position="none",
        plot_background=gg.element_rect(fill=background_color, color=background_color),
        panel_background=gg.element_rect(fill=background_color, color=background_color),
        plot_margin=0.02,
    )
)

(
    interactive(plot, hover_nearest=True)
    + css(from_dict={".tooltip": {"font-size": "1.2em"}})
    + save("docs/iframes/south-france-restaurants.html")
)