Download data Jalan di Openstreetmap menggunakan Jupyter Notebook

HidayatullahHidayatullah
4 min read

Kali ini kita akan menjelaskan tahapan membuat Geojson dari Jalan yang disediakan Openstreetmap. Data tersebut Kita olah dan Jadikan Satu file. Pertama Kali yaitu Kita mendownload data dari Jupyter Notebook.

import os
import requests
import geopandas as gpd
from shapely.geometry import LineString

# Folder penyimpanan data
data_folder = "data"
os.makedirs(data_folder, exist_ok=True)

# Query Overpass API untuk jalan di area tertentu
def fetch_osm_data(area_name, area_query):
    print(f"๐Ÿ“ฅ Mengunduh data jalan untuk {area_name}...")
    query = f"""
    [out:json];
    area[name="{area_query}"]->.searchArea;
    (way["highway"](area.searchArea););
    out body; >; out skel qt;
    """
    url = "http://overpass-api.de/api/interpreter"
    response = requests.get(url, params={"data": query})

    if response.status_code == 200:
        data = response.json()
        if "elements" in data and len(data["elements"]) > 0:
            print(f"โœ… Data untuk {area_name} berhasil diunduh.")
            return data
        else:
            print(f"โš ๏ธ Tidak ada data yang ditemukan untuk {area_name}")
    else:
        print(f"โŒ Gagal mengambil data untuk {area_name}")
    return None

# Konversi data dari Overpass API ke GeoDataFrame
def convert_to_gdf(osm_data):
    if not osm_data or "elements" not in osm_data:
        print("โš ๏ธ Data kosong atau tidak valid.")
        return None

    ways = []
    nodes = {node["id"]: (node["lon"], node["lat"]) for node in osm_data["elements"] if node["type"] == "node"}

    for element in osm_data["elements"]:
        if element["type"] == "way" and "nodes" in element:
            coordinates = [nodes[node_id] for node_id in element["nodes"] if node_id in nodes]
            if len(coordinates) > 1:
                ways.append({"geometry": LineString(coordinates), "highway": element.get("tags", {}).get("highway", "unknown")})

    if not ways:
        print("โš ๏ธ Tidak ada jalan yang ditemukan di data OSM.")
        return None

    return gpd.GeoDataFrame(ways, geometry="geometry", crs="EPSG:4326")

# Daftar wilayah yang akan diunduh, "x":"y", x mewakili official name, y mewakili name
districts = {
    "Kota Bandung": "Bandung",
    "Kabupaten Bandung": "Bandung",
    "Kota Cimahi": "Cimahi",
    "Kabupaten Bandung Barat": "Bandung Barat"
}

# Looping unduh data dan simpan
for area_name, area_query in districts.items():
    osm_data = fetch_osm_data(area_name, area_query)

    if osm_data:
        gdf = convert_to_gdf(osm_data)

        if gdf is not None and not gdf.empty:
            file_path = os.path.join(data_folder, f"{area_name.lower().replace(' ', '_')}_roads.geojson")
            gdf.to_file(file_path, driver="GeoJSON")
            print(f"๐Ÿ“ Data jalan untuk {area_name} berhasil disimpan di: {file_path}")
        else:
            print(f"โš ๏ธ Tidak ada data yang dapat disimpan untuk {area_name}")

print("๐ŸŽ‰ Semua proses selesai!")

Data bisa dicari ataupun ditambah dari Districts. dan Apabila terjadi error , bisa dicari di website osm. Kemudian bila berhasil, kita preview dulu hasilnya dengan kode dan hasilnya seperti dibawah.

import os
import geopandas as gpd
import matplotlib.pyplot as plt
import contextily as ctx
from tqdm import tqdm  # Untuk indikator loading

# Folder penyimpanan data
data_folder = "data"

# Daftar wilayah yang akan divisualisasikan
districts = [
    "kota_bandung_roads.geojson",
    "kabupaten_bandung_roads.geojson",
    "kota_cimahi_roads.geojson",
    "kabupaten_bandung_barat_roads.geojson"
]

# Buat figure utama
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
axes = axes.flatten()

# Looping dengan tqdm untuk progress bar
for idx, file_name in enumerate(tqdm(districts, desc="Memuat data")):
    file_path = os.path.join(data_folder, file_name)

    if os.path.exists(file_path):
        gdf = gpd.read_file(file_path)

        if not gdf.empty:
            # Konversi ke CRS Web Mercator agar sesuai dengan contextily
            gdf = gdf.to_crs(epsg=3857)

            # Plot data jalan
            ax = axes[idx]
            gdf.plot(ax=ax, linewidth=1, color="blue")
            ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.Mapnik)
            ax.set_title(file_name.replace("_", " ").replace(".geojson", "").title())
            ax.axis("off")
        else:
            print(f"โš ๏ธ File {file_name} kosong atau tidak valid.")
    else:
        print(f"โš ๏ธ File {file_name} tidak ditemukan.")

# Menampilkan plot
plt.tight_layout()
plt.show()

print("๐ŸŽ‰ Visualisasi selesai!")

Kemudian kita clip dengan cara seperti dibawah.

import os
import geopandas as gpd
import pandas as pd
from shapely.geometry import box

# Folder penyimpanan data
data_folder = "data"

# Daftar file GeoJSON yang akan digabungkan
districts = [
    "kota_bandung_roads.geojson",
    "kabupaten_bandung_roads.geojson",
    "kota_cimahi_roads.geojson",
    "kabupaten_bandung_barat_roads.geojson"
]

# Batas wilayah (xmin, ymin, xmax, ymax) untuk Bandung Raya
clip_bounds = [107.0934, -7.3432, 107.9791, -6.6032]  # Sesuaikan jika diperlukan

# Buat bounding box sebagai GeoDataFrame
clip_geom = gpd.GeoDataFrame(
    {"geometry": [box(*clip_bounds)]},
    crs="EPSG:4326"  # Pastikan CRS sesuai dengan GeoJSON yang digunakan
)

# List untuk menyimpan data dari masing-masing GeoJSON
gdf_list = []

# Loop untuk membaca dan menggabungkan data
for file_name in districts:
    file_path = os.path.join(data_folder, file_name)

    if os.path.exists(file_path):
        gdf = gpd.read_file(file_path)

        if not gdf.empty:
            gdf["source"] = file_name  # Tambahkan kolom sumber data
            gdf_list.append(gdf)
        else:
            print(f"โš ๏ธ File {file_name} kosong atau tidak valid.")
    else:
        print(f"โš ๏ธ File {file_name} tidak ditemukan.")

# Gabungkan semua GeoDataFrame menjadi satu
if gdf_list:
    merged_gdf = gpd.GeoDataFrame(pd.concat(gdf_list, ignore_index=True), crs="EPSG:4326")

    # Clip data dengan bounding box
    clipped_gdf = merged_gdf.clip(clip_geom)

    # Simpan hasil clip ke file GeoJSON baru
    clipped_geojson_path = os.path.join(data_folder, "clipped_roads_bandung_raya.geojson")
    clipped_gdf.to_file(clipped_geojson_path, driver="GeoJSON")

    print(f"โœ… Berhasil di-clip! File disimpan di: {clipped_geojson_path}")
else:
    print("โŒ Tidak ada data yang bisa di-clip.")

Setelah berhasil, baru kita preview hasilnya

Kemudian kita attach ketinggian dari dem dan tempelkan ke line dengan kode berikut:

import geopandas as gpd
import rasterio
import requests
import numpy as np
from rasterio.io import MemoryFile
from shapely.geometry import LineString, MultiLineString

# URL DEM (gantilah dengan sumber yang valid)
dem_url = "https://s3.amazonaws.com/elevation-tiles-prod/geotiff/10/550/344.tif"

# Mengunduh file DEM dari URL ke memori
response = requests.get(dem_url)
response.raise_for_status()

# Membuka file DEM langsung dari memori
with MemoryFile(response.content) as memfile:
    with memfile.open() as dem:

        # Baca data jalan (GeoJSON)
        geojson_path = "data/clipped_roads_bandung_raya.geojson"
        gdf = gpd.read_file(geojson_path)

        # Konversi CRS GeoDataFrame ke CRS DEM
        gdf = gdf.to_crs(dem.crs)

        # Fungsi untuk mendapatkan ketinggian rata-rata dari DEM
        def extract_elevation(geom):
            elevations = []

            if isinstance(geom, LineString):  # Jika LineString langsung ambil koordinatnya
                coords = list(geom.coords)
                elevations = list(dem.sample(coords))

            elif isinstance(geom, MultiLineString):  # Jika MultiLineString, pecah menjadi LineString
                for line in geom.geoms:
                    coords = list(line.coords)
                    elevations.extend(dem.sample(coords))  # Tambahkan semua sampel ketinggian

            elevations = np.array(elevations).flatten()

            return np.mean(elevations) if len(elevations) > 0 else None

        # Tambahkan kolom elevasi ke GeoDataFrame
        gdf["elevation"] = gdf.geometry.apply(extract_elevation)

        # Simpan hasilnya ke GeoJSON
        output_geojson = "data/roads_with_elevation_from_url.geojson"
        gdf.to_file(output_geojson, driver="GeoJSON")

print("โœ… Data jalan dengan ketinggian dari URL berhasil disimpan:", output_geojson)
0
Subscribe to my newsletter

Read articles from Hidayatullah directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Hidayatullah
Hidayatullah

Hi, my name is Hidayatullah. I am a GIS Engineer, Analyst, Specialist, and everything related to GIS. With over 5 years of experience, I am highly proficient in ArcGIS and QGIS. I specialize in spatial topology methods, least square adjustment measurement methods, PostGIS with PostgreSQL, RDBMS databases, spatial generalization by scale, WebGIS Geoserveer/Mapserver/Mapproxy, and more. If you're interested in my services, feel free to reach out via email at genhidayatullah@icloud.com.