Yuichi TAMIYA - Fab Futures 2025 - Data Science
Home About Tips

Final 1: My Camino Map¶

What is it about?¶

Showing my Camino, from Porto (15 Sep 2025) to Santiago de compostela (19 Sep 2025)

Dataset¶

A-GeoCat: Saint James' Way to Santiago de Compostela

The Way of St. James or St. James' Way (Caminho de Santiago, Camino de Santiago, Chemin de St-Jacques, Jakobsweg) is the pilgrimage route to the Cathedral of Santiago de Compostela in Galicia in northwestern Spain.

Files¶

Install GPKG(GeoPackage)

$ pip install geopandas
$ python -c "import geopandas as gpd; print(gpd.__version__)"
1.1.1
In [1]:
# How to verify that it works in JupyterLab

import geopandas as gpd
print("GeoPandas version:", gpd.__version__)
GeoPandas version: 1.1.1

Camino on a map of Spain and Portugal¶

Prompt(ChatGPT)
Create a sample that loads A-GeoCat’s GPKG in Python and overlays it on a map of Spain.

Answer
!pip install geopandas matplotlib

Download all 110m cultural themes orne_110m_admin_0_countries.zip from Admin 0 – Countries on https://www.naturalearthdata.com/downloads/110m-cultural-vectors/, then copy the .zip to the same dataset folder

The base code is generated

After several times of test, I added some parts in the code :

  • Add kmz from Google MyMap
  • Add missing segment
  • Draw markers and labels
In [2]:
# スペイン+ポルトガルを背景にして、カミーノ全ルートを重ねるコード
# カミーノ全ルート × スペイン+ポルトガル地図を重ねて描画

import geopandas as gpd
import pyogrio
import matplotlib.pyplot as plt

# ======================================
# 1. データのパス
# ======================================
gpkg_path = "datasets/finalproject/caminos_santiago.gpkg"
world_path = "datasets/finalproject/110m_cultural.zip"  # Natural Earth (countries)

# ======================================
# 2. スペイン+ポルトガルの境界線を読み込む
# ======================================
world = gpd.read_file(world_path, layer="ne_110m_admin_0_countries")

# Spain と Portugal のみ抽出
iberia = world[world["ADMIN"].isin(["Spain", "Portugal"])]

# 背景(スペイン+ポルトガル)
fig, ax = plt.subplots(figsize=(10, 10))
iberia.plot(ax=ax, color="lightgray", edgecolor="black", linewidth=0.5)

# ======================================
# 3. Camino GPKG のレイヤー一覧取得
# ======================================
layers = pyogrio.list_layers(gpkg_path)

# LineString のレイヤーだけ抽出
line_layers = [name for name, geom in layers if geom and "LINESTRING" in geom.upper()]

# print("📌 Camino line layers:", line_layers)

# ======================================
# 4. 各ルートをスペイン・ポルトガル地図の上に重ねて描画
# ======================================
for layer in line_layers:
    gdf = gpd.read_file(gpkg_path, layer=layer)
    gdf = gdf.to_crs(iberia.crs)   # 座標系を Iberia(スペイン&ポルトガル)に合わせる
    gdf.plot(ax=ax, linewidth=1)

plt.title("Camino de Santiago — All Routes on Spain & Portugal Map")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()
No description has been provided for this image

My Camino from Google My Map¶

Google my map

Export layers as kmz file and showing layers name¶

In [3]:
# Layer name 

import pyogrio

kmz_path = "datasets/finalproject/our_camino_map.kmz"

layers = pyogrio.list_layers(kmz_path)

# [('layer name', 'geometry type'])
for layer in layers:
    print(layer)
['1 (24.5km)+2 (14.0km)' 'Unknown']
['3 (24.5km)+4 (20.8km)+5 (26.8km)' 'Unknown']
['Tui - Redondela' 'Unknown']
['6 (23.5km)+7 (16.0km)+8A (22.6km) (Por Oficial)' 'Unknown']
['8B (20.8km) (Por Litoral)+9 (16.0km)' 'Unknown']
['10 (19.6km)+11(21.1km)' 'Unknown']
['12 (18.6km)+13 (24.4km)' 'Unknown']
['カミニャ から cociñarte へのルート' 'Unknown']
['Camino Portugués' 'Unknown']
['Stay' 'Unknown']

Ploting my camino on a map from Natural Earth¶

In [4]:
import geopandas as gpd
import pandas as pd
from shapely.ops import unary_union
import matplotlib.pyplot as plt

kmz_path = "datasets/finalproject/our_camino_map.kmz"

# 結合したいレイヤー名
target_layers = [
    "1 (24.5km)+2 (14.0km)",
    "3 (24.5km)+4 (20.8km)+5 (26.8km)",
    "Tui - Redondela",
    "10 (19.6km)+11(21.1km)",
    "12 (18.6km)+13 (24.4km)"
]

# ------------------------------
# ① 必要なレイヤーを読み込み
# ------------------------------
gdfs = []
for layer_name in target_layers:
    gdf = gpd.read_file(kmz_path, layer=layer_name)
    gdfs.append(gdf)

# ------------------------------
# ② 全ての gdf を1つにまとめる
# ------------------------------
merged_gdf = gpd.GeoDataFrame(
    pd.concat(gdfs, ignore_index=True),
    crs=gdfs[0].crs
)

# ------------------------------
# ③ 全ての LineString を 1 本に結合(unary_union)
#    → MultiLineString または LineString
# ------------------------------
merged_line = unary_union(merged_gdf.geometry)

# プロット用 GeoSeries
merged_geo = gpd.GeoSeries([merged_line], crs=merged_gdf.crs)

# ------------------------------
# ④ 背景地図(Spain / Portugal)
# ------------------------------
world = gpd.read_file(
    "datasets/finalproject/110m_cultural.zip", 
    layer="ne_110m_admin_0_countries"
)
spain = world[world["ADMIN"].isin(["Spain", "Portugal"])]

# ------------------------------
# ⑤ プロット
# ------------------------------
fig, ax = plt.subplots(figsize=(10, 12))

spain.plot(ax=ax, color="lightgray", edgecolor="black")

# 1本に統合した Camino ルート(赤)
merged_geo.plot(ax=ax, color="red", linewidth=1)

plt.title("Merged Google Maps Camino Route")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()
No description has been provided for this image

Ploting my camino and A-GeoCat on a map from Natural Earth¶

In [5]:
import geopandas as gpd
import pyogrio
import pandas as pd
from shapely.ops import unary_union
import matplotlib.pyplot as plt

# ======================================
# 1. data path
# ======================================
gpkg_path = "datasets/finalproject/caminos_santiago.gpkg"
world_path = "datasets/finalproject/110m_cultural.zip"
kmz_path = "datasets/finalproject/our_camino_map.kmz"

# ======================================
# 2. Background map (Spain + Portugal)
# 2. 背景地図(スペイン+ポルトガル)
# ======================================
world = gpd.read_file(world_path, layer="ne_110m_admin_0_countries")
iberia = world[world["ADMIN"].isin(["Spain", "Portugal"])]

fig, ax = plt.subplots(figsize=(12, 12))
iberia.plot(ax=ax, color="lightgray", edgecolor="black", linewidth=0.5)

# ======================================
# 3. Overlay all LineString layers from the Camino GPKG in blue
# 3. Camino(GPKG)すべての LineString を青で重ねる
# ======================================
layers = pyogrio.list_layers(gpkg_path)
line_layers = [name for name, geom in layers if geom and "LINESTRING" in geom.upper()]

for layer in line_layers:
    gdf = gpd.read_file(gpkg_path, layer=layer)
    gdf = gdf.to_crs(iberia.crs)
    gdf.plot(ax=ax, color="blue", linewidth=0.8, alpha=0.6)

# ======================================
# 4. Merge multiple routes from the Google KMZ and draw them in red
# 4. Google KMZ 内の複数ルートを結合して赤で描画
# ======================================
target_layers = [
    "1 (24.5km)+2 (14.0km)",
    "3 (24.5km)+4 (20.8km)+5 (26.8km)",
    "Tui - Redondela",
    "10 (19.6km)+11(21.1km)",
    "12 (18.6km)+13 (24.4km)"
]

gdfs = []
for layer_name in target_layers:
    gdf = gpd.read_file(kmz_path, layer=layer_name)
    gdfs.append(gdf)

# Merge all selected routes into a single line
# 全部を1つの GeoDataFrame に結合
merged_gdf = gpd.GeoDataFrame(
    pd.concat(gdfs, ignore_index=True),
    crs=gdfs[0].crs
)

# Merge all selected routes into a single line
# 結合して1本のラインへ
merged_line = unary_union(merged_gdf.geometry)
merged_geo = gpd.GeoSeries([merged_line], crs=merged_gdf.crs)

# Merge all selected routes into a single line
# 背景地図に合わせて CRS 変換
merged_geo = merged_geo.to_crs(iberia.crs)

# Google route (red)
# Google ルート(赤)
merged_geo.plot(ax=ax, color="red", linewidth=1)

# ======================================
# 5. Display the figure
# 5. 図を表示
# ======================================
plt.title("Camino de Santiago — All Routes + Google MyMaps Route")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()
No description has been provided for this image
In [6]:
import geopandas as gpd
import pyogrio
import pandas as pd
from shapely.ops import unary_union
import matplotlib.pyplot as plt
from shapely.geometry import LineString

# ======================================
# 1. Data paths / データパス
# ======================================
gpkg_path = "datasets/finalproject/caminos_santiago.gpkg"
world_path = "datasets/finalproject/110m_cultural.zip"
kmz_path = "datasets/finalproject/our_camino_map.kmz"

# ======================================
# 2. Background map (Spain + Portugal) / 背景地図(スペイン+ポルトガル)
# ======================================
world = gpd.read_file(world_path, layer="ne_110m_admin_0_countries")
iberia = world[world["ADMIN"].isin(["Spain", "Portugal"])]

fig, ax = plt.subplots(figsize=(12, 12))
iberia.plot(ax=ax, color="lightgray", edgecolor="black", linewidth=0.5)

# ======================================
# 3. Camino full routes from GPKG → blue / GPKGの全カミーノルート → 青で描画
# ======================================
layers = pyogrio.list_layers(gpkg_path)
line_layers = [name for name, geom in layers if geom and "LINESTRING" in geom.upper()]

for layer in line_layers:
    gdf = gpd.read_file(gpkg_path, layer=layer)
    gdf = gdf.to_crs(iberia.crs)
    gdf.plot(ax=ax, color="blue", linewidth=0.8, alpha=0.6)

# ======================================
# 4. Google KMZ route (merge multiple layers) / Google MyMaps の KMZ ルート(複数レイヤーを結合)
# ======================================
target_layers = [
    "1 (24.5km)+2 (14.0km)",
    "3 (24.5km)+4 (20.8km)+5 (26.8km)",
    "Tui - Redondela",
    "10 (19.6km)+11(21.1km)",
    "12 (18.6km)+13 (24.4km)"
]

gdfs = []
for layer_name in target_layers:
    gdf = gpd.read_file(kmz_path, layer=layer_name)
    gdfs.append(gdf)

# KMZ layers combined / KMZの複数レイヤーを結合
merged_gdf = gpd.GeoDataFrame(
    pd.concat(gdfs, ignore_index=True),
    crs=None   # MyMaps KMZ usually has no CRS / MyMapsのKMZは通常CRSがNoneになる
)

# --- Set CRS explicitly to WGS84 / KMZにCRS (WGS84) を明示的に設定 ---
merged_gdf = merged_gdf.set_crs("EPSG:4326")

# Merge into a single LineString / 一本のラインに統合
merged_line = unary_union(merged_gdf.geometry)

# ======================================
# 4b. Add missing red segment / 赤線の切れていた補完区間を追加
# ======================================
extra_coords = [
    (-8.83784, 41.8732),
    (-8.64664, 42.04916)
]

extra_line = LineString(extra_coords)

# Set CRS for extra line / 補完ラインにもCRSを設定
extra_gdf = gpd.GeoSeries([extra_line], crs="EPSG:4326")

# Merge original red route with extra segment / 元ルートと補完区間を結合
full_red_route = unary_union([merged_line, extra_gdf.iloc[0]])

# Convert to GeoSeries and reproject / GeoSeries化して座標系変換
full_red_geo = gpd.GeoSeries([full_red_route], crs="EPSG:4326")
full_red_geo = full_red_geo.to_crs(iberia.crs)

# Draw red route / 赤ルートを描画
full_red_geo.plot(ax=ax, color="red", linewidth=2)

# ======================================
# 5. Add start (Porte) & goal (Santiago) markers
#    出発点(Porte)と到着点(Santiago)にマーカーを追加
# ======================================
porte_lat = 41.087913089618894
porte_lon = -8.54339873871034

santiago_lat = 42.88206527707834
Santiago_lon = -8.55135182575899

# Draw markers and labels / マーカーとラベルを描く
ax.scatter(porte_lon, porte_lat, color="black", s=60, zorder=5)
ax.text(porte_lon, porte_lat, "Porte", fontsize=12, ha="left", va="bottom")

ax.scatter(Santiago_lon, santiago_lat, color="black", s=60, zorder=5)
ax.text(Santiago_lon, santiago_lat, "Santiago", fontsize=12, ha="left", va="bottom")

# ======================================
# 6. Show figure / 図の描画
# ======================================
plt.title("Camino de Santiago — All Routes + Google MyMaps Route + Start/Goal Points")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()
No description has been provided for this image

Porte to Santiago¶

In [7]:
import geopandas as gpd
import pyogrio
import pandas as pd
from shapely.ops import unary_union
from shapely.geometry import LineString, box
import matplotlib.pyplot as plt

# ======================================
# 1. Data paths / データパス
# ======================================
gpkg_path = "datasets/finalproject/caminos_santiago.gpkg"
world_path = "datasets/finalproject/110m_cultural.zip"
kmz_path = "datasets/finalproject/our_camino_map.kmz"

# ======================================
# 2. Background map: Spain + Portugal
# 2. 背景地図(スペイン+ポルトガル)
# ======================================
world = gpd.read_file(world_path, layer="ne_110m_admin_0_countries")
iberia = world[world["ADMIN"].isin(["Spain", "Portugal"])]

# CRS
target_crs = iberia.crs

# ======================================
# 3. Blue route layers (GPKG Camino routes)
# 3. 青ルート:GPKG の Camino 公式ルート
# ======================================
blue_layers = [
    "CPC — e01- A GUARDA - OIA",
    "CPC — e02- OIA- BAIONA",
    "CPC — e03- BAIONA - VIGO",
    "CPC — e04- VIGO - REDONDELA",
    "CPO — e01- TUI - O PORRIÑO",
    "CPO — e02- O PORRIÑO - REDONDELA",
    "CPO — e03- REDONDELA - PONTEVEDRA",
    "CPO — e04- PONTEVEDRA - CALDAS DE REIS",
    "CPO — e05- CALDAS DE REIS - PADRÓN",
    "CPO — e06- PADRÓN - SANTIAGO DE COMPOSTELA"
]

blue_gdfs = []
for layer in blue_layers:
    gdf = gpd.read_file(gpkg_path, layer=layer)
    gdf = gdf.to_crs(target_crs)
    blue_gdfs.append(gdf)

blue_routes = pd.concat(blue_gdfs, ignore_index=True)

# ======================================
# 4. Red route: Google KMZ + interpolation
# 4. 赤ルート:Google KMZ + 補完区間
# ======================================
target_layers = [
    "1 (24.5km)+2 (14.0km)",
    "3 (24.5km)+4 (20.8km)+5 (26.8km)",
    "Tui - Redondela",
    "10 (19.6km)+11(21.1km)",
    "12 (18.6km)+13 (24.4km)"
]

kmz_gdfs = []
for layer_name in target_layers:
    gdf = gpd.read_file(kmz_path, layer=layer_name)
    kmz_gdfs.append(gdf)

merged_gdf = gpd.GeoDataFrame(
    pd.concat(kmz_gdfs, ignore_index=True),
    crs="EPSG:4326"
)

merged_line = unary_union(merged_gdf.geometry)

# ---- interpolation line / 補完線 ----
extra_line = LineString([
    (-8.83784, 41.8732),
    (-8.64664, 42.04916)
])

# unify red route / 赤ルートを結合
full_red_route = unary_union([merged_line, extra_line])

# red → CRS transform / 座標系変換
full_red_geo = gpd.GeoSeries([full_red_route], crs="EPSG:4326")
full_red_geo = full_red_geo.to_crs(target_crs)

# ======================================
# 5. Trim map to area of interest
# 5. 地図を「Porte → Santiago」付近にトリミング
# ======================================
xmin, ymin = -9.5, 40.8
xmax, ymax = -8.3, 43.1
clip_box = box(xmin, ymin, xmax, ymax)

# ======================================
# 6. Plot
# ======================================
fig, ax = plt.subplots(figsize=(12, 12))

# Iberia clipped / イベリア半島のクリップ
iberia_clipped = gpd.clip(iberia, clip_box)
iberia_clipped.plot(ax=ax, color="lightgray", edgecolor="black")

# blue routes / 青ルート
gpd.clip(blue_routes, clip_box).plot(ax=ax, color="blue", linewidth=1, alpha=0.6)

# red route / 赤ルート
gpd.clip(full_red_geo, clip_box).plot(ax=ax, color="red", linewidth=2)

# start / goal
ax.scatter(-8.5433987, 41.087913, color="black", s=60)
ax.text(-8.5433987, 41.087913, "Porte", fontsize=12)

ax.scatter(-8.5513518, 42.882065, color="black", s=60)
ax.text(-8.5513518, 42.882065, "Santiago", fontsize=12)

plt.title("Camino de Santiago — Portuguese Coastal + Central Routes")
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()
No description has been provided for this image