This analysis is used to determine locations in Iowa that would be suitible for wind turbine power plant installation as well as to calculate the hypothetical potential wind energy production capacity of these areas. This type of analysis can be used as a baseline in determining the maximum cost and benefits for potential renewable energy projects.
import sqlalchemy as sa
import geopandas as gpd
import pandas as pd
import matplotlib.pyplot as plt
import contextily as ctx
import math
%matplotlib inline
= 'postgresql+psycopg2://{user}:{pwd}@{host}/{db_name}'
pg_uri_template = pg_uri_template.format(
db_uri ='postgresql+psycopg2',
drivername= '{ip address}',
host = '{username}',
user = '{pwd}',
pwd = 'osmiowa'
db_name
)= sa.create_engine(db_uri) engine
Database Name: - osmiowa
Database Tables: - planet_osm_line - planet_osm_point - planet_osm_polygon - planet_osm_roads - spatial_ref_sys - wind - wind_cells
Geometry: - Projected in EPSG:26975 - Geodetic CRS: NAD83 - Units: meters - Coordinate system: Cartesian 2D CS - Axes: easting, northing (X,Y) - Orientations: east, north
Residential Scenario 1 (3H)
= \
sql_buildings_residential_1 """
SELECT
ST_BUFFER(way, 450) as way
FROM
planet_osm_polygon
WHERE
building IN ('yes', 'residential', 'apartments', 'house', 'static_caravan', 'detached')
OR
landuse = 'residential'
OR
place = 'town'
"""
Residential Scenario 2 (10H)
= \
sql_buildings_residential_2 """
SELECT
ST_BUFFER(way, 1500) as way
FROM
planet_osm_polygon
WHERE
building
IN
('yes', 'residential', 'apartments', 'house', 'static_caravan', 'detached')
OR
landuse = 'residential'
OR
place = 'town'
"""
Non-residential (3H)
= \
sql_buildings_nonresidential """
SELECT
ST_BUFFER(way, 450) as way
FROM
planet_osm_polygon
WHERE
building
NOT IN
('yes', 'residential', 'apartments', 'house', 'static_caravan', 'detached')
"""
Airports
= \
sql_airports """
SELECT
ST_BUFFER(way, 7500) as way
FROM
planet_osm_polygon
WHERE
aeroway IS NOT NULL
"""
Military Bases
= \
sql_military """
SELECT
ST_BUFFER(way, 0) as way
FROM
planet_osm_polygon
WHERE
military IS NOT NULL
OR
landuse = 'military'
"""
Railways and Roads
= \
sql_railways_n_roads """
SELECT
ST_BUFFER(way, 300) as way
FROM
planet_osm_line
WHERE
(railway IS NOT NULL
AND railway
NOT IN ('abandoned', 'disused', 'razed', 'dismantled'))
OR
(highway IS NOT NULL
AND
highway
IN
('motorway', 'motorway_link', 'trunk', 'trunk_link', 'road',
'primary', 'primary_link', 'secondary', 'secondary_link'))
"""
Nature Reserves, Parks, and Wetlands
= \
sql_nature """
SELECT
way
FROM
planet_osm_polygon
WHERE
leisure
IN
('nature_reserve', 'park')
OR
"natural"
IN
('wetland')
"""
Rivers
= \
sql_rivers """
SELECT
ST_BUFFER(way, 150) as way
FROM
planet_osm_line
WHERE
waterway
IN
('river')
"""
Lakes
= \
sql_lakes """
SELECT
way
FROM
planet_osm_polygon
WHERE
water
IN
('lake', 'reservoir', 'pond')
"""
Power Lines
= \
sql_powerlines """
SELECT
ST_BUFFER(way, 300) as way
FROM
planet_osm_line
WHERE
power IS NOT NULL
"""
Power Plants
= \
sql_powerplants """
SELECT
ST_BUFFER(way, 150) as way
FROM
planet_osm_polygon
WHERE
power IS NOT NULL
"""
Wind Turbines
= \
sql_turbines """
SELECT
ST_BUFFER(way, 680) as way
FROM
planet_osm_point
WHERE
"generator:source" IS NOT NULL
AND
"generator:source" IN ('wind')
"""
= \
sql_siting_constraints f"""
{sql_buildings_residential_1}
UNION
{sql_buildings_nonresidential}
UNION
{sql_airports}
UNION
{sql_military}
UNION
{sql_railways_n_roads}
UNION
{sql_nature}
UNION
{sql_rivers}
UNION
{sql_lakes}
UNION
{sql_powerlines}
UNION
{sql_powerplants}
UNION
{sql_turbines}
"""
= gpd.read_postgis(sql_siting_constraints, con = engine, geom_col = 'way') siting_constraints
= \
sql_siting_constraints_2 f"""
{sql_buildings_residential_2}
UNION
{sql_buildings_nonresidential}
UNION
{sql_airports}
UNION
{sql_military}
UNION
{sql_railways_n_roads}
UNION
{sql_nature}
UNION
{sql_rivers}
UNION
{sql_lakes}
UNION
{sql_powerlines}
UNION
{sql_powerplants}
UNION
{sql_turbines}
"""
= gpd.read_postgis(sql_siting_constraints_2, con = engine, geom_col = 'way') siting_constraints_2
= plt.subplots(figsize=(8, 8))
fig, ax = ax, markersize = .1, color = "purple", alpha = .5)
siting_constraints.plot(ax True, color = 'dimgray')
ax.grid('Siting Constraints (Scenario 1)', fontsize=12)
ax.set_title(= [-5, 5])
ax.ticklabel_format(scilimits ="EPSG:26975") ctx.add_basemap(ax, crs
= plt.subplots(figsize=(8, 8))
fig, ax = ax, markersize = .1, color = "purple", alpha = .5)
siting_constraints_2.plot(ax True, color = 'dimgray')
ax.grid('Siting Constraints (Scenario 2)', fontsize=12)
ax.set_title(= [-5, 5])
ax.ticklabel_format(scilimits ="EPSG:26975") ctx.add_basemap(ax, crs
The table wind_cells_10000 contains 10 km2 square polygons with associated average annual wind speeds (m s-1), arranged to cover Iowa in a ragged grid.
= \
sql_wind_speeds """
SELECT *
FROM
wind_cells_10000
WHERE
wind_speed IS NOT NULL
"""
= gpd.read_postgis(sql_wind_speeds, con = engine, geom_col = 'geom') wind_speeds
= wind_speeds.overlay(siting_constraints, how = 'difference', keep_geom_type = False) suitable_cells
= plt.subplots(figsize=(8, 8))
fig, ax = ax, markersize = .1, color = "purple", alpha = .5)
suitable_cells.plot(ax True, color = 'dimgray')
ax.grid('Suitable Areas (Scenario 1)', fontsize=12)
ax.set_title(= [-5, 5])
ax.ticklabel_format(scilimits ="EPSG:26975") ctx.add_basemap(ax, crs
= math.pi * (136 * 5) ** 2
rotor_5x "area"] = suitable_cells['geom'].area
suitable_cells["number_wind_turbines"] = suitable_cells['geom'].area / rotor_5x suitable_cells[
= suitable_cells["number_wind_turbines"].sum()
total_wind_turbines print("The total number of turbines in scenario 1 is:", round(total_wind_turbines))
The total number of turbines in scenario 1 is: 57286
'energy_per_turbine'] = 2.6 * suitable_cells['wind_speed'] - 5
suitable_cells['power_per_cell'] = suitable_cells['energy_per_turbine'] * suitable_cells['number_wind_turbines'] suitable_cells[
= suitable_cells['power_per_cell'].sum()
total_scenario_1 print("The total power produced in scenario 1 is:", round(total_scenario_1), "GWH/year")
The total power produced in scenario 1 is: 1064159 GWH/year
= wind_speeds.overlay(siting_constraints_2, how = 'difference', keep_geom_type = False) suitable_cells_2
= plt.subplots(figsize=(8, 8))
fig, ax = ax, markersize = .1, color = "purple", alpha = .5)
suitable_cells_2.plot(ax True, color = 'dimgray')
ax.grid('Suitable Areas (Scenario 2)', fontsize=12)
ax.set_title(= [-5, 5])
ax.ticklabel_format(scilimits ="EPSG:26975") ctx.add_basemap(ax, crs
= math.pi * (136 * 5) ** 2
rotor_5x "area"] = suitable_cells_2['geom'].area
suitable_cells_2["number_wind_turbines"] = suitable_cells_2['geom'].area / rotor_5x suitable_cells_2[
= suitable_cells_2["number_wind_turbines"].sum()
total_wind_turbines_2 print("The total number of turbines in scenario 2 is:", round(total_wind_turbines_2))
The total number of turbines in scenario 2 is: 52055
'energy_per_turbine'] = 2.6 * suitable_cells_2['wind_speed'] - 5
suitable_cells_2['power_per_cell'] = suitable_cells_2['energy_per_turbine'] * suitable_cells_2['number_wind_turbines']
suitable_cells_2[ wind_speeds
id | geom | wind_speed | |
---|---|---|---|
0 | 1 | POLYGON ((1256222.769 1212179.582, 1266222.769… | 9.336039 |
1 | 2 | POLYGON ((1266222.769 1212179.582, 1276222.769… | 9.097315 |
2 | 3 | POLYGON ((1276222.769 1212179.582, 1286222.769… | 8.984566 |
3 | 4 | POLYGON ((1286222.769 1212179.582, 1296222.769… | 9.266137 |
4 | 5 | POLYGON ((1296222.769 1212179.582, 1306222.769… | 9.296747 |
… | … | … | … |
1437 | 1438 | POLYGON ((1686222.769 902179.582, 1696222.769 … | 8.678687 |
1438 | 1439 | POLYGON ((1426222.769 892179.582, 1436222.769 … | 9.114730 |
1439 | 1440 | POLYGON ((1656222.769 892179.582, 1666222.769 … | 8.237385 |
1440 | 1441 | POLYGON ((1666222.769 892179.582, 1676222.769 … | 8.423034 |
1441 | 1442 | POLYGON ((1666222.769 882179.582, 1676222.769 … | 8.109468 |
1442 rows × 3 columns
= suitable_cells_2['power_per_cell'].sum()
total_scenario_2 print("The total power produced in scenario 2 is:", round(total_scenario_2), " GWH/year")
The total power produced in scenario 2 is: 967009 GWH/year
For attribution, please cite this work as
Molitor (2021, Dec. 3). Cullen Molitor: Iowa's Wind Power Potential. Retrieved from cullen-molitor.github.io/posts/2021-12-03-iowas-wind-power-potential/
BibTeX citation
@misc{molitor2021iowa's, author = {Molitor, Cullen}, title = {Cullen Molitor: Iowa's Wind Power Potential}, url = {cullen-molitor.github.io/posts/2021-12-03-iowas-wind-power-potential/}, year = {2021} }