Introduction¶
This notebook demonstrates how to use GLAES to systematically place objects (such as wind turbines) within a predefined study area.
Workflow Overview:¶
- Create file with available lands like in 00_basic_workflow.
- Distribute item placements within the eligible regions
- basic placement
- spare the edges of a region
- direction-dependent placements
- get placement coordinates
- use wind direction data for placements
- spatially varying raster
- individual placement areas
# Import GLAES
import glaes as gl
import geokit as gk
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
#other imports
from copy import copy
/fast/home/t-cronenberg/miniforge3/envs/glaes_new/lib/python3.13/site-packages/geokit/core/raster.py:17: UserWarning: A NumPy version >=1.23.5 and <2.3.0 is required for this version of SciPy (detected version 2.3.5) from scipy.interpolate import RectBivariateSpline
1. Create the land eligibility analysis¶
Here the eligibile land file from the first example notebook is recreated, for details have a look into 00_basic_workflow.
# Choose a region to operate on (Here, a predefined region for Aachen, Germany is used)
regionPath = gl._test_data_["aachenShapefile.shp"]
# Initialize ExclusionCalculator object
ec = gl.ExclusionCalculator(regionPath, srs=3035, pixelRes=100)
# Apply exclusions
ec.excludeRasterType(gl._test_data_["clc-aachen_clipped.tif"], value=(12,22))
ec.excludeVectorType(gl._test_data_["aachenRoads.shp"], where="type='motorway' OR type='primary' OR type='trunk'", buffer=200)
# Draw result
ec.draw()
Memory usage during calc: 238.0234375 MB Memory usage during calc: 260.8203125 MB
<Axes: >
Copy the ec object after land eligibility analysis.
# make a copy of the ec object so we can "reset" the ec object after distributing placements and have a "clean" ec again
ec_backup = copy(ec)
2. Distribute item placements within the eligible regions¶
Basic Placement¶
"Separation" specifies the minimal distance between two objects.
# Do placements
ec.distributeItems(separation=1000)
# Draw result
ec.draw()
<Axes: >
Spare the edges of a region¶
Sometimes it is needed to spare the edges of a region so that the separation distances are maintained also between placements of neighbouring regions (avoidRegionBorders=True).
# reset the ec object to remove the "old" placements from above
ec = copy(ec_backup)
# repeat the step above and set the argument avoidRegionBorders to True
ec.distributeItems(separation=1000, avoidRegionBorders=True)
# Draw result. In comparison to above you can see that no placements are distributed towards the edges of the region, you can also see that less items were distributed
ec.draw()
Memory usage during calc: 279.15234375 MB
<Axes: >
Direction-dependent placements¶
If two different separation distances are defined, they represent the minimum required spacing between two objects along two perpendicular directions: an axial direction and a transverse direction. The axialDirection parameter specifies the orientation of the axial axis in degrees, measured counter-clockwise relative to East. For example, 0 aligns the axial direction east–west, 45 aligns it north-east–south-west, and 90 aligns it north–south. When both axial and transverse distances are provided, the effective buffer distance applied at the boundary is the average of the two separation distances.
# reset the ec object again
ec = copy(ec_backup)
# do placements with "separation=(axial, transverse), axialDirection"
ec.distributeItems(separation=(1200, 600), axialDirection=45, avoidRegionBorders=True)
# Draw result
ec.draw()
450 Memory usage during calc: 280.62109375 MB
<Axes: >
Get the placements as XY coordinates¶
#print only the first 5 placements
print(ec.itemCoords[:5])
[[4053200.01 3098499.99] [4053550. 3098000.01] [4054800.01 3097999.99] [4051400.01 3097899.99] [4051850. 3097500.01]]
Use wind direction data for placements¶
ec.distributeItems(
(1400, 600),
axialDirection=r"data/ERA5_wind_direction_100m_mean_Aachen.tif",
outputSRS=gk.srs.EPSG4326 )
ec.draw()
<Axes: >
Create a spatially varying raster and use it to apply location-dependent spacing when distributing items.¶
# Create a raster with values increasing from 50 to 200 from top to bottom
mat = np.zeros_like(ec.region.mask, dtype=np.uint8)
for i in range(mat.shape[0]):
mat[i,:] = (200-50)*i/(mat.shape[0]) + 50
# Show the raster
h = plt.imshow(mat)
plt.colorbar(h)
plt.show()
# Convert the array into a raster used to scale separation distances
rotor_diam_raster = ec.region.createRaster(output="tmp/rotor_diameter.tif", data=mat)
# Place items using a variable spacing:
# effective spacing = 8.0 × local raster value
points = ec.distributeItems(
separation=8.0,
sepScaling=rotor_diam_raster)
ec.draw()
<Axes: >
Individual placement areas¶
Additionally, the whole area can be divided into one area for each placement by using the distributeAreas function. Those can be saved as well. To call this function, first use the distributeItems function to create placements as elaborated above.
# 1) # Create the placement points and save them as a shapefile
points = ec.distributeItems(separation=1000)
ec.saveItems(output="tmp/individual_placement_points.shp")
# 2) Create placement areas (polygons around placed items) and save them as a shapefile
areas = ec.distributeAreas(minArea=300000)
ec.saveAreas(output="tmp/individual_placement_areas.shp")
# 3) Visualisation
ec.draw()
<Axes: >