Vector data analysis with GeoPandas
Introduction
GeoPandas is an open-source Python library designed to read, manipulate, analyze, and export vector spatial data within tabular structures. It extends pandas DataFrames with a geometry column, delegating geometric calculations to Shapely and file I/O operations to GDAL-backed drivers.
Consequently, multi-step desktop GIS operations—opening layers, filtering by attributes, reprojecting coordinates, and joining points to administrative polygons—collapse into concise script statements. Rerunning the script replicates the identical workflow across fresh datasets. The package is installed via pip install geopandas or conda-forge; establishing an isolated virtual environment for each project prevents dependency conflicts.
GeoDataFrame structure
The core object in GeoPandas is the GeoDataFrame. Each row represents a single geographic feature, while each column stores an attribute. Exactly one column serves as the active geometry column, holding vector primitives such as points, lines, or polygons. The table also maintains explicit Coordinate Reference System (CRS) metadata.
This structure closely mirrors a GIS attribute table. The key difference is that filtering, grouping, and aggregations leverage pandas vector syntax. The GeoPandas user guide covers GeoDataFrame and GeoSeries mechanics in depth.

Reading data and initial inspection
Layers are loaded using the read_file function, which supports standard vector formats like GeoPackage, Shapefile, and GeoJSON. Before running analytical operations, inspect data health:
import geopandas as gpd
maktablar = gpd.read_file("data/maktablar.gpkg")
tumanlar = gpd.read_file("data/tumanlar.gpkg")
print(maktablar.crs) # for example, EPSG:4326
print(len(maktablar), maktablar.geom_type.unique())
print((~maktablar.is_valid).sum(), maktablar.geometry.isna().sum())
These checks reveal CRS definitions, record counts, geometry types, and any invalid or null shapes. If row counts look abnormal or the CRS returns None, subsequent spatial calculations become untrustworthy. When a layer lacks a defined CRS, verify the correct authority code from source metadata rather than guessing.
Filtering by attributes
Subsetting a GeoDataFrame uses standard pandas indexing. Rows matching the conditional statement retain their spatial attributes in a new GeoDataFrame:
katta = maktablar[maktablar["oquvchi_soni"] > 1000]
noaniq = maktablar[maktablar["oquvchi_soni"].isna()]
The second statement is essential: null (NaN) attribute records are excluded from numeric comparisons and drop out silently. Always count and report missing attributes explicitly. In text attributes, formatting discrepancies (such as "Yunusobod" versus "Yunusobod tumani") similarly reduce query yields.
CRS and spatial measurements
Calculating area or length in geographic angular coordinates produces meaningless square degrees or degree distances. Layers must be projected into a metric coordinate system prior to measurement. For Tashkent, Samarkand, and regions spanning 66–72° East longitude, WGS 84 / UTM zone 42N (EPSG:32642) is standard:
maktablar = maktablar.to_crs(32642)
tumanlar = tumanlar.to_crs(32642)
tumanlar["maydon_km2"] = tumanlar.area / 1_000_000
to_crs transforms vertex coordinates mathematically, whereas set_crs merely reassigns the metadata definition—confusing the two is a frequent error. Uzbekistan spans four UTM zones (40N–43N) from west to east: Khorezm falls in 41N, while eastern Andijan reaches 43N. For nationwide analyses, select a unified national projected CRS beforehand. This topic is covered in coordinate systems and reprojection in GIS.

Spatial join
A spatial join links two layers based on geometric relationships rather than shared table keys. For example, assigning each school point its corresponding administrative district name:
natija = gpd.sjoin(maktablar, tumanlar[["tuman", "geometry"]],
how="left", predicate="within")
print(natija["tuman"].isna().sum()) # features not matched to any district
print(natija.groupby("tuman").size()) # school counts grouped by district
The how="left" parameter preserves all school points, leaving unmatched records as null. The within predicate excludes points lying strictly on boundary perimeters; intersects is safer along border zones. If district polygons overlap, schools can duplicate, making output row validation against source counts necessary.

Saving results
Save analytical outputs into dedicated destination files rather than overwriting source data. The GeoPackage format supports multiple layers, long field names, and embedded CRS specifications:
natija.to_file("natija/maktablar_tuman.gpkg", layer="maktablar")
Opening the exported dataset in QGIS to visually inspect samples supplements programmatic validation. Recording library versions and source paths alongside analysis scripts guarantees exact reproducibility.
Conclusion
GeoPandas provides tabular ergonomics for vector layers: reading, validating, reprojecting, joining, and saving proceed within a clean Python script.
Script dependability depends not on complex syntax, but on strict CRS discipline, geometry validation, and deliberate spatial predicate selection.

