Skills › Scientific Agent Skills › geomaster › core-libraries.md
core-libraries.md
277 lines · skills/geomaster/references/core-libraries.md at 5f2bc03
1# Core Geospatial Libraries
3Local API reference. File-dependent fragments are illustrative; see [review.md](review.md)
4for tested versions. Raster masks, band meaning and grid alignment are prerequisites.
5Metric geometry operations require a suitable projected CRS with metre units.
Core Geospatial Libraries
Local API reference. File-dependent fragments are illustrative; see review.md for tested versions. Raster masks, band meaning and grid alignment are prerequisites. Metric geometry operations require a suitable projected CRS with metre units.
Raw lines 1 to 67## GDAL (Geospatial Data Abstraction Library)
9GDAL is the foundation for geospatial I/O in Python.
11```python
12from osgeo import gdal
13gdal.UseExceptions()
15# Open a raster file
16ds = gdal.Open('raster.tif')
17band = ds.GetRasterBand(1)
18data = band.ReadAsArray()
20# Get geotransform
21geotransform = ds.GetGeoTransform()
22origin_x = geotransform[0]
23pixel_width = geotransform[1]
25# Get projection
26proj = ds.GetProjection()
27```
GDAL (Geospatial Data Abstraction Library)
GDAL is the foundation for geospatial I/O in Python.
from osgeo import gdal
gdal.UseExceptions()
# Open a raster file
ds = gdal.Open('raster.tif')
band = ds.GetRasterBand(1)
data = band.ReadAsArray()
# Get geotransform
geotransform = ds.GetGeoTransform()
origin_x = geotransform[0]
pixel_width = geotransform[1]
# Get projection
proj = ds.GetProjection()Raw lines 7 to 2829## Rasterio
31Rasterio provides a cleaner interface to GDAL.
33```python
34import rasterio
35from rasterio.mask import mask
36import numpy as np
38# Basic reading
39with rasterio.open('raster.tif') as src:
40 data = src.read(masked=True) # All bands: (bands, rows, columns)
41 band1 = src.read(1) # Single band
42 profile = src.profile # Metadata
44# Windowed reading (memory efficient)
45with rasterio.open('large.tif') as src:
46 window = ((0, 100), (0, 100))
47 subset = src.read(1, window=window)
49# Writing
50with rasterio.open('output.tif', 'w',
51 driver='GTiff',
52 height=data.shape[1],
53 width=data.shape[2],
54 count=data.shape[0],
55 dtype=data.dtype,
56 crs=profile['crs'],
57 transform=profile['transform']) as dst:
58 dst.write(data, masked=True)
60# Masking
61with rasterio.open('raster.tif') as src:
62 masked_data, cropped_transform = mask(src, shapes=[polygon], crop=True, filled=False)
63```
Rasterio
Rasterio provides a cleaner interface to GDAL.
import rasterio
from rasterio.mask import mask
import numpy as np
# Basic reading
with rasterio.open('raster.tif') as src:
data = src.read(masked=True) # All bands: (bands, rows, columns)
band1 = src.read(1) # Single band
profile = src.profile # Metadata
# Windowed reading (memory efficient)
with rasterio.open('large.tif') as src:
window = ((0, 100), (0, 100))
subset = src.read(1, window=window)
# Writing
with rasterio.open('output.tif', 'w',
driver='GTiff',
height=data.shape[1],
width=data.shape[2],
count=data.shape[0],
dtype=data.dtype,
crs=profile['crs'],
transform=profile['transform']) as dst:
dst.write(data, masked=True)
# Masking
with rasterio.open('raster.tif') as src:
masked_data, cropped_transform = mask(src, shapes=[polygon], crop=True, filled=False)Raw lines 29 to 6465## Fiona
67Fiona handles vector data I/O.
69```python
70import fiona
72# Read features
73with fiona.open('data.geojson') as src:
74 for feature in src:
75 geom = feature['geometry']
76 props = feature['properties']
78# Get schema and CRS
79with fiona.open('data.shp') as src:
80 schema = src.schema
81 crs = src.crs
83# Write data
84schema = {'geometry': 'Point', 'properties': {'name': 'str'}}
85with fiona.open('output.geojson', 'w', driver='GeoJSON',
86 schema=schema, crs='EPSG:4326') as dst:
87 dst.write({
88 'geometry': {'type': 'Point', 'coordinates': [0, 0]},
89 'properties': {'name': 'Origin'}
90 })
91```
Fiona
Fiona handles vector data I/O.
import fiona
# Read features
with fiona.open('data.geojson') as src:
for feature in src:
geom = feature['geometry']
props = feature['properties']
# Get schema and CRS
with fiona.open('data.shp') as src:
schema = src.schema
crs = src.crs
# Write data
schema = {'geometry': 'Point', 'properties': {'name': 'str'}}
with fiona.open('output.geojson', 'w', driver='GeoJSON',
schema=schema, crs='EPSG:4326') as dst:
dst.write({
'geometry': {'type': 'Point', 'coordinates': [0, 0]},
'properties': {'name': 'Origin'}
})Raw lines 65 to 9293## Shapely
95Shapely provides geometric operations.
97```python
98from shapely.geometry import Point, LineString, Polygon
99from shapely.ops import unary_union
101# Create geometries
102point = Point(0, 0)
103line = LineString([(0, 0), (1, 1)])
104poly = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
106# Geometric operations
107buffered = point.buffer(1) # Buffer
108simplified = poly.simplify(0.01) # Simplify
109centroid = poly.centroid # Centroid
110intersection = poly1.intersection(poly2) # Intersection
112# Spatial relationships
113point.within(poly) # True if point inside polygon
114poly1.intersects(poly2) # True if geometries intersect
115poly1.contains(poly2) # True if poly2 inside poly1
117# Unary union
118combined = unary_union([poly1, poly2, poly3])
120# Buffer with different joins
121buffer_round = point.buffer(1, quad_segs=16)
122buffer_mitre = point.buffer(1, mitre_limit=1, join_style=2)
123```
Shapely
Shapely provides geometric operations.
from shapely.geometry import Point, LineString, Polygon
from shapely.ops import unary_union
# Create geometries
point = Point(0, 0)
line = LineString([(0, 0), (1, 1)])
poly = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
# Geometric operations
buffered = point.buffer(1) # Buffer
simplified = poly.simplify(0.01) # Simplify
centroid = poly.centroid # Centroid
intersection = poly1.intersection(poly2) # Intersection
# Spatial relationships
point.within(poly) # True if point inside polygon
poly1.intersects(poly2) # True if geometries intersect
poly1.contains(poly2) # True if poly2 inside poly1
# Unary union
combined = unary_union([poly1, poly2, poly3])
# Buffer with different joins
buffer_round = point.buffer(1, quad_segs=16)
buffer_mitre = point.buffer(1, mitre_limit=1, join_style=2)Raw lines 93 to 124125## PyProj
127PyProj handles coordinate transformations.
129```python
130from pyproj import Transformer, CRS
132# Consistent longitude/latitude order; these points are in UTM zone 10N.
133transformer = Transformer.from_crs('EPSG:4326', 'EPSG:32610', always_xy=True)
134x, y = transformer.transform(-122.4, 37.7, errcheck=True)
135lon, lat = transformer.transform(x, y, direction='INVERSE', errcheck=True)
136x_array, y_array = transformer.transform([-122.4, -122.3], [37.7, 37.8])
137# Height is a third transform argument, not an always_z option. A horizontal-only
138# transform can leave Z unchanged; vertical conversion needs a suitable compound CRS/grid.
140# Get CRS info
141crs = CRS.from_epsg(4326)
142print(crs.name) # WGS 84
143print(crs.axis_info) # Axis info
145# Custom transformation
146transformer = Transformer.from_pipeline(
147 'proj=pipeline step inv proj=utm zone=32 ellps=WGS84 step proj=unitconvert xy_in=rad xy_out=deg'
148)
149```
PyProj
PyProj handles coordinate transformations.
from pyproj import Transformer, CRS
# Consistent longitude/latitude order; these points are in UTM zone 10N.
transformer = Transformer.from_crs('EPSG:4326', 'EPSG:32610', always_xy=True)
x, y = transformer.transform(-122.4, 37.7, errcheck=True)
lon, lat = transformer.transform(x, y, direction='INVERSE', errcheck=True)
x_array, y_array = transformer.transform([-122.4, -122.3], [37.7, 37.8])
# Height is a third transform argument, not an always_z option. A horizontal-only
# transform can leave Z unchanged; vertical conversion needs a suitable compound CRS/grid.
# Get CRS info
crs = CRS.from_epsg(4326)
print(crs.name) # WGS 84
print(crs.axis_info) # Axis info
# Custom transformation
transformer = Transformer.from_pipeline(
'proj=pipeline step inv proj=utm zone=32 ellps=WGS84 step proj=unitconvert xy_in=rad xy_out=deg'
)Raw lines 125 to 150151## GeoPandas
153GeoPandas combines pandas with geospatial capabilities.
155```python
156import geopandas as gpd
158# Reading data
159gdf = gpd.read_file('data.geojson')
160gdf = gpd.read_file('data.shp', encoding='utf-8')
161gdf = gpd.read_postgis('SELECT * FROM data', con=engine)
163# Writing data
164gdf.to_file('output.geojson', driver='GeoJSON')
165gdf.to_file('output.gpkg', layer='data', use_arrow=True)
167# CRS operations
168gdf.crs # Get CRS
169if gdf.crs is None:
170 raise ValueError('Confirm the source CRS from authoritative metadata before transforming')
171gdf = gdf.to_crs('EPSG:32633') # Example target: use only within zone 33N
172# set_crs labels existing coordinates without transforming them. Use it only when
173# the missing source CRS is independently known, before calling to_crs.
175# Geometric operations
176gdf['area'] = gdf.geometry.area
177gdf['length'] = gdf.geometry.length
178gdf['buffer'] = gdf.geometry.buffer(100)
179gdf['centroid'] = gdf.geometry.centroid
181# Spatial joins
182joined = gpd.sjoin(gdf1, gdf2, how='inner', predicate='intersects')
183joined = gpd.sjoin_nearest(gdf1, gdf2, max_distance=1000)
185# Overlay operations
186intersection = gpd.overlay(gdf1, gdf2, how='intersection')
187union = gpd.overlay(gdf1, gdf2, how='union')
188difference = gpd.overlay(gdf1, gdf2, how='difference')
190# Dissolve
191dissolved = gdf.dissolve(by='region', aggfunc='sum')
193# Clipping
194clipped = gpd.clip(gdf, mask_gdf)
196# Spatial indexing (for performance)
197idx = gdf.sindex
198possible_matches = idx.intersection(polygon.bounds)
199```
GeoPandas
GeoPandas combines pandas with geospatial capabilities.
import geopandas as gpd
# Reading data
gdf = gpd.read_file('data.geojson')
gdf = gpd.read_file('data.shp', encoding='utf-8')
gdf = gpd.read_postgis('SELECT * FROM data', con=engine)
# Writing data
gdf.to_file('output.geojson', driver='GeoJSON')
gdf.to_file('output.gpkg', layer='data', use_arrow=True)
# CRS operations
gdf.crs # Get CRS
if gdf.crs is None:
raise ValueError('Confirm the source CRS from authoritative metadata before transforming')
gdf = gdf.to_crs('EPSG:32633') # Example target: use only within zone 33N
# set_crs labels existing coordinates without transforming them. Use it only when
# the missing source CRS is independently known, before calling to_crs.
# Geometric operations
gdf['area'] = gdf.geometry.area
gdf['length'] = gdf.geometry.length
gdf['buffer'] = gdf.geometry.buffer(100)
gdf['centroid'] = gdf.geometry.centroid
# Spatial joins
joined = gpd.sjoin(gdf1, gdf2, how='inner', predicate='intersects')
joined = gpd.sjoin_nearest(gdf1, gdf2, max_distance=1000)
# Overlay operations
intersection = gpd.overlay(gdf1, gdf2, how='intersection')
union = gpd.overlay(gdf1, gdf2, how='union')
difference = gpd.overlay(gdf1, gdf2, how='difference')
# Dissolve
dissolved = gdf.dissolve(by='region', aggfunc='sum')
# Clipping
clipped = gpd.clip(gdf, mask_gdf)
# Spatial indexing (for performance)
idx = gdf.sindex
possible_matches = idx.intersection(polygon.bounds)Raw lines 151 to 200Common Workflows
Raw lines 201 to 202203### Batch Reprojection
205```python
206import geopandas as gpd
207from pathlib import Path
209input_dir = Path('input')
210output_dir = Path('output')
211output_dir.mkdir(parents=True, exist_ok=True)
213for shp in input_dir.glob('*.shp'):
214 gdf = gpd.read_file(shp)
215 gdf = gdf.to_crs('EPSG:32633')
216 gdf.to_file(output_dir / shp.name)
217```
Batch Reprojection
import geopandas as gpd
from pathlib import Path
input_dir = Path('input')
output_dir = Path('output')
output_dir.mkdir(parents=True, exist_ok=True)
for shp in input_dir.glob('*.shp'):
gdf = gpd.read_file(shp)
gdf = gdf.to_crs('EPSG:32633')
gdf.to_file(output_dir / shp.name)Raw lines 203 to 218219### Raster to Vector Conversion
221```python
222import rasterio.features
223import geopandas as gpd
224from shapely.geometry import shape
226with rasterio.open('raster.tif') as src:
227 image = src.read(1)
228 valid = src.read_masks(1) > 0
229 crs = src.crs
230 results = (
231 {'properties': {'value': v}, 'geometry': s}
232 for s, v in rasterio.features.shapes(image, mask=valid, transform=src.transform)
233 )
235geoms = list(results)
236gdf = gpd.GeoDataFrame.from_features(geoms, crs=crs)
237```
Raster to Vector Conversion
import rasterio.features
import geopandas as gpd
from shapely.geometry import shape
with rasterio.open('raster.tif') as src:
image = src.read(1)
valid = src.read_masks(1) > 0
crs = src.crs
results = (
{'properties': {'value': v}, 'geometry': s}
for s, v in rasterio.features.shapes(image, mask=valid, transform=src.transform)
)
geoms = list(results)
gdf = gpd.GeoDataFrame.from_features(geoms, crs=crs)Raw lines 219 to 238239### Vector to Raster Conversion
241```python
242from rasterio.features import rasterize
243import geopandas as gpd
245gdf = gpd.read_file('polygons.gpkg')
246shapes = ((geom, 1) for geom in gdf.geometry)
248raster = rasterize(
249 shapes,
250 out_shape=(height, width),
251 transform=transform,
252 fill=0,
253 dtype=np.uint8
254)
255```
Vector to Raster Conversion
from rasterio.features import rasterize
import geopandas as gpd
gdf = gpd.read_file('polygons.gpkg')
shapes = ((geom, 1) for geom in gdf.geometry)
raster = rasterize(
shapes,
out_shape=(height, width),
transform=transform,
fill=0,
dtype=np.uint8
)Raw lines 239 to 256257### Combining Multiple Rasters
259```python
260import rasterio.merge
261import rasterio as rio
262from contextlib import ExitStack
264files = ['tile1.tif', 'tile2.tif', 'tile3.tif']
265with ExitStack() as stack:
266 datasets = [stack.enter_context(rio.open(f)) for f in files]
267 # Verify matching CRS, band count/type and compatible grids before merging.
268 merged, transform = rasterio.merge.merge(datasets)
269 profile = datasets[0].profile.copy()
270 profile.update(transform=transform, height=merged.shape[1], width=merged.shape[2])
271 with rio.open('merged.tif', 'w', **profile) as dst:
272 dst.write(merged)
273```
275For more detailed examples, see [code-examples.md](code-examples.md).
277Sources: [Rasterio masks](https://rasterio.readthedocs.io/en/stable/topics/masks.html), [PyProj Transformer](https://pyproj4.github.io/pyproj/stable/api/transformer.html), [GeoPandas API](https://geopandas.org/en/stable/docs/reference.html).
Combining Multiple Rasters
import rasterio.merge
import rasterio as rio
from contextlib import ExitStack
files = ['tile1.tif', 'tile2.tif', 'tile3.tif']
with ExitStack() as stack:
datasets = [stack.enter_context(rio.open(f)) for f in files]
# Verify matching CRS, band count/type and compatible grids before merging.
merged, transform = rasterio.merge.merge(datasets)
profile = datasets[0].profile.copy()
profile.update(transform=transform, height=merged.shape[1], width=merged.shape[2])
with rio.open('merged.tif', 'w', **profile) as dst:
dst.write(merged)For more detailed examples, see code-examples.md.
Sources: Rasterio masks, PyProj Transformer, GeoPandas API.
Raw lines 257 to 277