๐บ๏ธ แแแกแแฎแแแแแแก แกแแแญแแแ แแแแก แแแแแแแแ แแ แแแแฃแแแแแแชแแยถ
ipyleaflet + GeoPandas | Google Colab
แแก notebook แแกแ แฃแแแแก PyQGIS-แแก แแแแแแแแฃแ แแแแชแแแแก โ แแแกแแฎแแแแแแก แกแแแญแแแ แแแแก แแแแแแแแแก โ แกแฃแคแแ Python แแแ แแแแจแ, Google Colab-แแ.
๐ แคแแ แแฃแแยถ
$$D = \frac{P}{A}$$
แกแแแแช:
- D โ แแแกแแฎแแแแแแก แกแแแญแแแ แแแ (แแ/แแ.แแ)
- P โ แแแกแแฎแแแแแแก แ แแแแแแแแ (
Mosaxl_200) - A โ แคแแ แแแแ แแ. แแแแแแแขแ แจแ (
area_km)
๐ง แแแแแงแแแแแฃแแ แแแแแแแแแแแแยถ
| แแแแแแแแแแ | แแแแแแ |
|---|---|
geopandas |
แกแแแ แชแแแ แแแแแชแแแแแแก แแแแฃแจแแแแแ (QGIS-แแก แแแแแแแ) |
ipyleaflet |
แแแขแแ แแฅแขแแฃแแ แ แฃแแ Jupyter/Colab-แจแ |
branca |
แคแแ แแก แกแแแแ (colormap) แแแแแแแแกแแแแก |
pandas |
แแขแ แแแฃแขแแแแก แชแฎแ แแแ |
numpy |
แ แแชแฎแแแแ แแแแแแแแแแ |
๐ฆ แแแแแฏแ 1 โ แแแแแแแแแแแแแก แแแกแขแแแแชแแยถ
Google Colab-แแ
geopandasแแipyleafletแฌแแแแกแฌแแ แแแงแแแแแ แกแญแแ แแแแ.
# แแแแแแแแแแแแแก แแแกแขแแแแชแแ Google Colab-แแ
!pip install geopandas ipyleaflet branca --quiet
๐ฅ แแแแแฏแ 2 โ แแแแแแแแแแแแแก แแแแแ แขแยถ
import geopandas as gpd
import pandas as pd
import numpy as np
import json
import requests
import ipyleaflet
from ipyleaflet import (
Map, GeoJSON, Choropleth, LegendControl,
WidgetControl, LayersControl, basemaps
)
from ipywidgets import HTML, Output
import branca.colormap as cm
print(f"โ
geopandas v{gpd.__version__}")
print(f"โ
ipyleaflet v{ipyleaflet.__version__}")
print(f"โ
pandas v{pd.__version__}")
๐บ๏ธ แแแแแฏแ 3 โ แกแแฅแแ แแแแแแก แแฃแแแชแแแแแแขแแขแแแแก แแแแแชแแแแแแก แฉแแขแแแ แแแยถ
แแแงแแแแแ GADM (Global Administrative Areas) แแแแแก แกแแฅแแ แแแแแแก แแ-2 แแแแแก แแแแแแแกแขแ แแชแแฃแแ แแ แแแฃแแแแแก GeoJSON-แก.
แแ แแแแแแแแจแ แกแแแแแขแแแฃแ แ (Mosaxl_200) แแแกแแฎแแแแแแก แแแแแชแแแแแ แแแแขแแแ, PyQGIS แแแแแแแแแก แแแแแแแแฃแ แแ.
# -------------------------------------------------------
# แกแแฅแแ แแแแแแก แแฃแแแชแแแแแแขแแขแแแแก GeoJSON-แแก แฉแแขแแแ แแแ
# GADM v4.1 โ Georgia, Admin Level 2 (แแฃแแแชแแแแแแขแแขแแแ)
# -------------------------------------------------------
GEOJSON_URL = (
"https://raw.githubusercontent.com/nvkelso/natural-earth-vector/"
"master/geojson/ne_10m_admin_1_states_provinces.geojson"
)
# แฉแแแแขแแแ แแแ แแ แคแแแขแ แแชแแ แกแแฅแแ แแแแแแกแแแแก
gdf_world = gpd.read_file(GEOJSON_URL)
gdf = gdf_world[gdf_world['admin'] == 'Georgia'].copy()
gdf = gdf.reset_index(drop=True)
print(f"๐ แฉแแขแแแ แแฃแแ แแแแแฅแขแแแแก แ แแแแแแแแ: {len(gdf)}")
print(f"๐ CRS: {gdf.crs}")
gdf[['name', 'geometry']].head()
๐ข แแแแแฏแ 4 โ แกแแแแแขแแแฃแ แ แแแกแแฎแแแแแแก แแแแแชแแแแแแก แแแแแขแแแยถ
PyQGIS แแแแแแแแแก แแแแแแแแฃแ แ แแแแ Mosaxl_200 โ แแแกแแฎแแแแแแก แแแแแชแแแ 2000-แแแแ แฌแแแแแกแแแแก.
แ แแแแฃแ แ แแ แแแฅแขแแกแแแแก แแก แแแแ GeoPackage/Shapefile-แจแ แฃแแแ แแ แกแแแแแแแก.
# -------------------------------------------------------
# แกแแแแแขแแแฃแ แ แแแกแแฎแแแแแแก แแแแแชแแแแแ (Mosaxl_200)
# แกแแฅแแ แแแแแแก แ แแแแแแแแแก แกแแแแ แแฃแแ แแแกแแฎแแแแแ 2002 แฌ.
# -------------------------------------------------------
np.random.seed(42)
# แ แแแแฃแ แ แกแแฅแแ แแแแแแก แ แแแแแแฃแแ แแแกแแฎแแแแแแก แกแแแแ แแฃแแ แแแแจแแแแแแแแแ
population_map = {
"Tbilisi": 1081679,
"Adjara": 376016,
"Guria": 113350,
"Imereti": 699666,
"Kakheti": 407182,
"Kvemo Kartli": 497531,
"Mtskheta-Mtianeti": 125141,
"Racha-Lechkhumi and Kvemo Svaneti": 50969,
"Samegrelo-Zemo Svaneti": 466.100,
"Samtskhe-Javakheti": 207598,
"Shida Kartli": 314801,
"Abkhazia": 215272,
"South Ossetia": 70000
}
def assign_population(name):
"""แแแกแแฎแแแแแแก แแแแแญแแแ แกแแฎแแแแก แแแฎแแแแแ, แกแฎแแ แจแแแแฎแแแแแจแ โ แจแแแแฎแแแแแแ"""
for key, val in population_map.items():
if key.lower() in str(name).lower() or str(name).lower() in key.lower():
return int(val)
return np.random.randint(50_000, 500_000)
gdf['Mosaxl_200'] = gdf['name'].apply(assign_population)
print("โ
Mosaxl_200 แแแแ แแแแแขแแแฃแแแ")
gdf[['name', 'Mosaxl_200']].sort_values('Mosaxl_200', ascending=False)
๐ แแแแแฏแ 5 โ แคแแ แแแแแก แแแแแแแแ แแ แแแกแแฎแแแแแแก แกแแแญแแแ แแแยถ
PyQGIS-แแก แแแแแแแ โ $area, "area"/1000000, "Mosaxl_200"/area_km:
# QGIS-แจแ:
expr1 = QgsExpression('$area') # แแ. แแแขแ แ
expr2 = QgsExpression('"area"/1000000') # แแ. แแ
expr3 = QgsExpression('"Mosaxl_200"/area_km') # แกแแแญแแแ แแแ
# GeoPandas-แจแ แแก แแกแ:
gdf['area'] = gdf.to_crs(epsg=32638).area # แแ. แแแขแ แ
gdf['area_km'] = gdf['area'] / 1_000_000 # แแ. แแ
gdf['simwidrove'] = gdf['Mosaxl_200'] / gdf['area_km'] # แแ/แแ.แแ
# -------------------------------------------------------
# แคแแ แแแแแก แแแแแแแแ
# EPSG:32638 โ UTM Zone 38N (แกแแฅแแ แแแแแแกแแแแก แจแแกแแคแแ แแกแ)
# -------------------------------------------------------
# แกแแฌแงแแกแ CRS แจแแแแฎแแ (WGS84 โ ipyleaflet-แแกแแแแก)
gdf_wgs84 = gdf.copy()
# แคแแ แแแแแก แแแแแแแแ แแ แแแฅแชแแฃแ แกแแกแขแแแแจแ (แแ. แแแขแ แ)
gdf_projected = gdf.to_crs(epsg=32638)
gdf_wgs84['area'] = gdf_projected.area # expr1: $area
gdf_wgs84['area_km'] = gdf_wgs84['area'] / 1_000_000 # expr2: "area"/1000000
gdf_wgs84['simwidrove'] = ( # expr3: "Mosaxl_200"/area_km
gdf_wgs84['Mosaxl_200'] / gdf_wgs84['area_km']
)
print("โ
แแแแแแ แแแแแแแแแแแ:")
print(f" area โ แคแแ แแแแ แแ. แแแขแ แจแ")
print(f" area_km โ แคแแ แแแแ แแ. แแแแแแแขแ แจแ")
print(f" simwidrove โ แแแกแแฎแแแแแแก แกแแแญแแแ แแแ (แแ/แแ.แแ)")
print()
# แจแแแแแแแแก แชแฎแ แแแ
result_df = gdf_wgs84[['name', 'Mosaxl_200', 'area_km', 'simwidrove']].copy()
result_df['area_km'] = result_df['area_km'].round(2)
result_df['simwidrove'] = result_df['simwidrove'].round(2)
result_df.sort_values('simwidrove', ascending=False).reset_index(drop=True)
๐บ๏ธ แแแแแฏแ 6 โ ipyleaflet Choropleth แ แฃแแยถ
แแแแฃแแแแแแชแแ choropleth แขแแแแก แ แฃแแแ โ แกแแแญแแแ แแแแก แแแแจแแแแแแแ แคแแ แแก แแแขแแแกแแแแแแ.
- ๐ก แฆแแ แงแแแแแแ โ แแแแแแ แกแแแญแแแ แแแ
- ๐ด แแฃแฅแ แฌแแแแแ โ แแแฆแแแ แกแแแญแแแ แแแ
hover-แแ แแแแแฉแแแแแ แแฃแแแชแแแแแแขแแขแแก แกแแฎแแแ แแ แกแแแญแแแ แแแ.
# -------------------------------------------------------
# GeoJSON แแแแแแแแแ ipyleaflet-แแกแแแแก
# -------------------------------------------------------
# GeoDataFrame-แแก GeoJSON-แแ แแแ แแแฅแแแ
geojson_data = json.loads(gdf_wgs84.to_json())
# แกแแแญแแแ แแแแก dictionary (Choropleth-แแกแแแแก)
choro_data = dict(
zip(
gdf_wgs84['name'].astype(str),
gdf_wgs84['simwidrove'].round(2)
)
)
print(f"โ
GeoJSON แแแแแแแแแฃแแแ โ {len(geojson_data['features'])} แแแแแฅแขแ")
print("\n๐ แกแแแญแแแ แแแแก แแแแจแแแแแแแแแ (แแ/แแ.แแ):")
for name, val in sorted(choro_data.items(), key=lambda x: x[1], reverse=True):
bar = "โ" * min(int(val / 50), 30)
print(f" {name:<40} {val:>8.1f} {bar}")
# -------------------------------------------------------
# ipyleaflet Choropleth แ แฃแแแก แจแแฅแแแ
# -------------------------------------------------------
# แคแแ แแก แกแแแแ (YlOrRd โ แงแแแแแแ โ แแแ แแแฏแแกแคแแ แ โ แฌแแแแแ)
colormap = cm.LinearColormap(
colors=['#ffffb2', '#fecc5c', '#fd8d3c', '#f03b20', '#bd0026'],
vmin=gdf_wgs84['simwidrove'].min(),
vmax=gdf_wgs84['simwidrove'].quantile(0.95), # outlier-แแก แแแแแแ
caption='แแแกแแฎแแแแแแก แกแแแญแแแ แแแ (แแ/แแ.แแ)'
)
# แ แฃแแแก แจแแฅแแแ โ แกแแฅแแ แแแแแแก แชแแแขแ แ
m = Map(
center=(42.0, 43.5),
zoom=7,
basemap=basemaps.CartoDB.Positron,
scroll_wheel_zoom=True
)
# Choropleth แจแ แ
choropleth = Choropleth(
geo_data=geojson_data,
choro_data=choro_data,
colormap=colormap,
key_on='feature.properties.name',
border_color='white',
style={
'fillOpacity': 0.75,
'weight': 1.5,
'dashArray': '3'
},
hover_style={
'fillOpacity': 0.95,
'weight': 3,
'dashArray': '0'
},
name='แแแกแแฎแแแแแแก แกแแแญแแแ แแแ'
)
m.add_layer(choropleth)
# แคแแ แแก แกแแแแแก แแแแแแแ
colormap.add_to(m)
# แจแ แแแแแก แแแแขแ แแแ
m.add_control(LayersControl(position='topright'))
# แกแแแแฃแ แ
title_html = HTML(
value="""
<div style='
background: rgba(255,255,255,0.92);
padding: 10px 16px;
border-radius: 8px;
border-left: 4px solid #bd0026;
font-family: sans-serif;
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
'>
<b style='font-size:14px;'>๐บ๏ธ แกแแฅแแ แแแแแ</b><br>
<span style='font-size:12px; color:#555;'>แแแกแแฎแแแแแแก แกแแแญแแแ แแแ โ แแ/แแ.แแ</span>
</div>
"""
)
m.add_control(WidgetControl(widget=title_html, position='topleft'))
# Hover แแแคแ แแแแแแ
info_html = HTML(
value="<div style='font-family:sans-serif; font-size:12px; color:#777; padding:6px;'>hover แ แแแแแแแ</div>"
)
info_widget = WidgetControl(widget=info_html, position='bottomleft')
m.add_control(info_widget)
def on_hover(event, feature, **kwargs):
"""Hover-แแ แแแคแแก แฉแแแแแแ"""
name = feature['properties'].get('name', '?')
density = choro_data.get(name, 0)
area = gdf_wgs84.loc[gdf_wgs84['name'] == name, 'area_km'].values
pop = gdf_wgs84.loc[gdf_wgs84['name'] == name, 'Mosaxl_200'].values
area_str = f"{area[0]:,.0f}" if len(area) else "?"
pop_str = f"{pop[0]:,}" if len(pop) else "?"
info_html.value = f"""
<div style='
background: rgba(255,255,255,0.95);
padding: 10px 14px;
border-radius: 6px;
font-family: sans-serif;
font-size: 12px;
border-left: 3px solid #bd0026;
min-width: 180px;
'>
<b style='font-size:13px;'>{name}</b><br>
๐ฅ แแแกแแฎแแแแแ: <b>{pop_str}</b><br>
๐ แคแแ แแแแ: <b>{area_str} แแ.แแ</b><br>
๐ แกแแแญแแแ แแแ: <b>{density:.1f} แแ/แแ.แแ</b>
</div>
"""
choropleth.on_hover(on_hover)
print("โ
แ แฃแแ แแแแแแ โ แฅแแแแแ แแแฎแแแ แแแแฃแแแแแแชแแแก")
m
๐ แแแแแฏแ 7 โ แจแแแแแแแแก แชแฎแ แแแยถ
PyQGIS Attribute Table-แแก แแแแแแแ โ แกแ แฃแแ แแแแแแแแแแ แแขแ แแแฃแขแแแ.
# -------------------------------------------------------
# แจแแแแแแแแก แชแฎแ แแแ โ Attribute Table (QGIS แแแแแแแ)
# -------------------------------------------------------
display_df = gdf_wgs84[[
'name', 'Mosaxl_200', 'area', 'area_km', 'simwidrove'
]].copy()
display_df.columns = [
'แ แแแแแแ', 'แแแกแแฎแแแแแ (Mosaxl_200)',
'แคแแ แแแแ (แแ.แ)', 'แคแแ แแแแ (แแ.แแ)', 'แกแแแญแแแ แแแ (แแ/แแ.แแ)'
]
display_df['แคแแ แแแแ (แแ.แ)'] = display_df['แคแแ แแแแ (แแ.แ)'].apply(lambda x: f"{x:,.0f}")
display_df['แคแแ แแแแ (แแ.แแ)'] = display_df['แคแแ แแแแ (แแ.แแ)'].apply(lambda x: f"{x:,.2f}")
display_df['แกแแแญแแแ แแแ (แแ/แแ.แแ)'] = display_df['แกแแแญแแแ แแแ (แแ/แแ.แแ)'].apply(lambda x: f"{x:.2f}")
display_df['แแแกแแฎแแแแแ (Mosaxl_200)'] = display_df['แแแกแแฎแแแแแ (Mosaxl_200)'].apply(lambda x: f"{x:,}")
display_df.reset_index(drop=True, inplace=True)
display_df
๐พ แแแแแฏแ 8 โ แจแแแแแแแแก แจแแแแฎแแยถ
แแแแแแแแแแ แแแแแชแแแแแแก แแฅแกแแแ แขแ GeoJSON แแ CSV แคแแ แแแขแแแจแ.
# -------------------------------------------------------
# แจแแแแแแแแก แแฅแกแแแ แขแ
# -------------------------------------------------------
# GeoJSON แจแแแแฎแแ (QGIS-แจแ แแแฎแกแแแกแแแแก)
export_cols = ['name', 'Mosaxl_200', 'area', 'area_km', 'simwidrove', 'geometry']
gdf_export = gdf_wgs84[export_cols].copy()
gdf_export.to_file('municipalitys_density.geojson', driver='GeoJSON')
print("โ
GeoJSON แจแแแแฎแฃแแแ: municipalitys_density.geojson")
# CSV แจแแแแฎแแ (แแขแ แแแฃแขแแแแก แชแฎแ แแแ)
csv_cols = ['name', 'Mosaxl_200', 'area', 'area_km', 'simwidrove']
gdf_wgs84[csv_cols].to_csv('municipalitys_density.csv', index=False, encoding='utf-8-sig')
print("โ
CSV แจแแแแฎแฃแแแ: municipalitys_density.csv")
# Google Colab-แแ แฉแแแแขแแแ แแแ
try:
from google.colab import files
files.download('municipalitys_density.csv')
files.download('municipalitys_density.geojson')
print("๐ฅ แคแแแแแแ แฉแแแแแขแแแ แแแแ...")
except ImportError:
print("โน๏ธ Colab แแแ แแแ แแ แแ แแก โ แคแแแแแแ แแแแแแแแ แ แแแ แแฅแขแแ แแแจแ แจแแแแแฎแแแ")
๐ แจแแฏแแแแแ โ PyQGIS vs GeoPandas แจแแแแ แแแยถ
| แแแแ แแชแแ | PyQGIS | GeoPandas (Colab) |
|---|---|---|
| แจแ แแก แแแฎแกแแ | QgsProject.instance().mapLayersByName() |
gpd.read_file() |
| แแแแแก แแแแแขแแแ | pv.addAttributes([QgsField(...)]) |
gdf['แแแแ'] = ... |
| แคแแ แแแแ | QgsExpression('$area') |
gdf.to_crs(32638).area |
| แคแแ แแแแ แแ.แแ | QgsExpression('"area"/1000000') |
gdf['area'] / 1_000_000 |
| แกแแแญแแแ แแแ | QgsExpression('"Mosaxl_200"/area_km') |
gdf['Mosaxl_200'] / gdf['area_km'] |
| แแขแ แแแฃแขแแแแก แแแแแฎแแแแ | layer.updateFeature(i) |
แแแขแแแแขแฃแ แ (pandas) |
| แแแแฃแแแแแแชแแ | QGIS Layer Styling | ipyleaflet.Choropleth |
| แแฅแกแแแ แขแ | Save As... | gdf.to_file() / df.to_csv() |
๐ แจแแแแจแแแ: แ แแแแฃแ แ แแ แแแฅแขแแกแแแแก
Mosaxl_200แแแแ GeoPackage แแ Shapefile-แจแ
แฃแแแ แแงแแก โgpd.read_file('Municipalitys.gpkg')แแ แงแแแแ แกแฎแแ แแแแแฏแ
แแฃแกแขแแ แแกแแ แแแแแงแแแแแฃแแ.
osdoc.qgis.ge | PyQGIS แแฃแ แกแ