Area and AreaBorder Model-DF Generators¶
AreaModelGenerator
¶
Bases: GeoModelGeneratorBase
Generates comprehensive area model DataFrames from node-to-area mappings.
This class creates detailed area model DataFrames that aggregate node-level data into area-level representations for energy system analysis. It supports automatic area discovery, node counting, and geographic representative point calculation for visualization and spatial analysis.
The generator processes node model data with area assignments to create comprehensive area models suitable for energy system aggregation, market analysis, and spatial visualization workflows.
Key Features
- Automatic area discovery from node-to-area mappings
- Representative geographic point calculation for visualization
- Integration with geometric area data (polygons, boundaries)
- Support for different area granularities (countries, bidding zones, regions)
- Robust handling of missing or incomplete area assignments
MESQUAL Integration
Designed to work with MESQUAL's area accounting system, providing area model building capabilities that support spatial energy system analysis, capacity aggregation, and visualization workflows.
Attributes:
| Name | Type | Description |
|---|---|---|
node_model_df |
DataFrame
|
Node-level data with area assignments |
area_column |
str
|
Column name containing area identifiers |
geo_location_column |
str
|
Column name containing geographic Point objects |
Examples:
Basic area model generation:
>>> import pandas as pd
>>> from shapely.geometry import Point
>>>
>>> # Create node model with area assignments
>>> node_data = pd.DataFrame({
>>> 'voltage': [380, 380, 220, 380, 220],
>>> 'country': ['DE', 'DE', 'FR', 'FR', 'BE'],
>>> 'capacity_mw': [2000, 1500, 800, 1200, 600],
>>> 'location': [Point(10, 52), Point(11, 53), Point(2, 48),
>>> Point(3, 49), Point(4, 50)]
>>> }, index=['DE1', 'DE2', 'FR1', 'FR2', 'BE1'])
>>>
>>> # Generate area model
>>> generator = AreaModelGenerator(node_data, 'country')
>>> area_model = generator.generate_area_model()
>>> print(area_model)
node_count projection_point
country
DE 2 POINT (10.5 52.5)
FR 2 POINT (2.5 48.5)
BE 1 POINT (4 50)
Enhanced area model with geometry:
>>> import geopandas as gpd
>>> from shapely.geometry import Polygon
>>>
>>> # Create area geometries
>>> area_polygons = gpd.GeoDataFrame({
>>> 'geometry': [
>>> Polygon([(9, 51), (12, 51), (12, 54), (9, 54)]), # DE
>>> Polygon([(1, 47), (4, 47), (4, 50), (1, 50)]), # FR
>>> Polygon([(3, 49), (5, 49), (5, 51), (3, 51)]) # BE
>>> ]
>>> }, index=['DE', 'FR', 'BE'])
>>>
>>> # Enhance with geometry
>>> area_model_geo = generator.enhance_with_geometry(area_model, area_polygons)
>>> print(f"Enhanced model has geometry: {'geometry' in area_model_geo.columns}")
Custom enhancement workflow:
>>> # Step-by-step area model building
>>> base_model = generator.generate_base_area_model_from_area_names_in_node_model_df()
>>> enhanced_model = generator.enhance_area_model_df_by_adding_node_count_per_area(base_model)
>>> final_model = generator.enhance_area_model_df_by_adding_representative_geo_point(enhanced_model)
>>> print(f"Created area model with {len(final_model)} areas")
Energy Domain Context
- Area models are fundamental for energy system analysis, enabling:
- Projection of node-level data to area-level data (e.g. nodal prices -> area prices)
- Market zone aggregation and analysis
- Regional energy balance studies
- ...
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/area_model_generator.py
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | |
__init__
¶
__init__(node_model_df: DataFrame, area_column: str, geo_location_column: str = None)
Initialize the area model generator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_model_df
|
DataFrame
|
DataFrame containing node-level data with area assignments. Must contain area_column with area identifiers for each node. May contain geographic Point objects for spatial analysis. |
required |
area_column
|
str
|
Column name in node_model_df containing area assignments (e.g., 'country', 'bidding_zone', 'market_region', 'control_area'). |
required |
geo_location_column
|
str
|
Column name containing geographic Point objects for representative point calculation. If None, automatically detects column containing Point geometries. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If area_column is not found in node_model_df columns. |
Example:
>>> import pandas as pd
>>> from shapely.geometry import Point
>>>
>>> # Node data with area assignments
>>> nodes = pd.DataFrame({
>>> 'voltage_kv': [380, 220, 380, 150],
>>> 'country': ['DE', 'DE', 'FR', 'FR'],
>>> 'bidding_zone': ['DE_LU', 'DE_LU', 'FR', 'FR'],
>>> 'coordinates': [Point(10, 52), Point(11, 53), Point(2, 48), Point(3, 49)]
>>> }, index=['DE1', 'DE2', 'FR1', 'FR2'])
>>>
>>> # Initialize for country-level analysis
>>> generator = AreaModelGenerator(nodes, 'country', 'coordinates')
>>>
>>> # Or let it auto-detect geographic column
>>> generator = AreaModelGenerator(nodes, 'bidding_zone')
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/area_model_generator.py
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
generate_base_area_model_from_area_names_in_node_model_df
¶
generate_base_area_model_from_area_names_in_node_model_df() -> DataFrame
Generate base area model DataFrame from unique area names in node data.
Creates a minimal area model DataFrame containing only the unique area identifiers found in the node model data. This forms the foundation for building comprehensive area models.
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Base area model with area identifiers as index. Contains no additional columns - serves as starting point for enhancement with node counts, geographic data, etc. |
Example:
>>> generator = AreaModelGenerator(node_data, 'country')
>>> base_model = generator.generate_base_area_model_from_area_names_in_node_model_df()
>>> print(base_model)
Empty DataFrame
Columns: []
Index: ['DE', 'FR', 'BE']
Note
Areas with None or NaN values in the area_column are excluded from the generated model.
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/area_model_generator.py
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | |
ensure_completeness_of_area_model_df
¶
ensure_completeness_of_area_model_df(area_model_df: DataFrame) -> DataFrame
Ensure area model contains all areas present in node data.
Validates and extends an existing area model DataFrame to include any areas found in the node data that might be missing from the provided area model. This is useful when working with predefined area models that may not cover all areas in the dataset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_model_df
|
DataFrame
|
Existing area model DataFrame to validate and extend. |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Complete area model containing all areas from node data. Existing data is preserved, new areas are added with NaN values for existing columns. |
Example:
>>> # Predefined area model missing some areas
>>> partial_model = pd.DataFrame({
>>> 'max_price': [5000, 3000]
>>> }, index=['DE', 'FR'])
>>>
>>> # Ensure completeness (adds 'BE' if present in node data)
>>> complete_model = generator.ensure_completeness_of_area_model_df(partial_model)
>>> print(complete_model)
max_price
country
DE 5000
FR 3000
BE NaN
Use Case
Essential for maintaining data consistency when combining predefined area models with dynamic node-based area discovery.
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/area_model_generator.py
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 | |
enhance_area_model_df_by_adding_node_count_per_area
¶
enhance_area_model_df_by_adding_node_count_per_area(area_model_df: DataFrame, node_count_column_name: str = 'node_count') -> DataFrame
Enhance area model by adding node count statistics per area.
Aggregates the number of nodes assigned to each area and adds this information to the area model DataFrame. Node counts are essential for understanding infrastructure density and capacity distribution.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_model_df
|
DataFrame
|
Base area model DataFrame to enhance. |
required |
node_count_column_name
|
str
|
Name for the new node count column. Defaults to 'node_count'. |
'node_count'
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Enhanced area model with node count column added. Existing data is preserved, node counts are added for all areas. Areas not present in node data will have NaN node counts. |
Example:
>>> base_model = generator.generate_base_area_model_from_area_names_in_node_model_df()
>>> enhanced_model = generator.enhance_area_model_df_by_adding_node_count_per_area(base_model)
>>> print(enhanced_model)
node_count
country
DE 2
FR 2
BE 1
>>> # Custom column name
>>> enhanced_model = generator.enhance_area_model_df_by_adding_node_count_per_area(
>>> base_model, 'infrastructure_count'
>>> )
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/area_model_generator.py
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | |
enhance_area_model_df_by_adding_representative_geo_point
¶
enhance_area_model_df_by_adding_representative_geo_point(area_model_df: DataFrame | GeoDataFrame, target_column_name: str = 'projection_point', round_point_decimals: int = 4) -> DataFrame
Enhance area model by adding representative geographic points for labeling and KPI printing in map visualizations.
Calculates representative geographic points for each area based on either area geometries (if available) or node locations within each area. These points are useful for visualization, labeling, and spatial analysis.
The method supports two calculation modes: 1. Geometry-based: Uses area polygon centroids or representative points 2. Node-based: Calculates centroid from node locations within each area
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_model_df
|
DataFrame | GeoDataFrame
|
Area model DataFrame to enhance. Can be regular DataFrame or GeoDataFrame with 'geometry' column. |
required |
target_column_name
|
str
|
Name for the new representative point column. Defaults to 'projection_point'. |
'projection_point'
|
round_point_decimals
|
int
|
Number of decimal places for coordinate rounding. Set to None to disable rounding. Defaults to 4. |
4
|
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Enhanced area model with representative points added. Points are added as Shapely Point objects suitable for mapping and spatial analysis. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If geo_location_column contains non-Point objects. |
Example:
>>> # Node-based representative points
>>> enhanced_model = generator.enhance_area_model_df_by_adding_representative_geo_point(base_model)
>>> print(enhanced_model)
projection_point
country
DE POINT (10.5 52.5)
FR POINT (2.5 48.5)
BE POINT (4 50)
>>> # With custom column name and precision
>>> enhanced_model = generator.enhance_area_model_df_by_adding_representative_geo_point(
>>> base_model, 'center_point', round_point_decimals=2
>>> )
>>> # Access coordinates for mapping
>>> center = enhanced_model.loc['DE', 'projection_point']
>>> print(f"DE center: {center.x:.2f}, {center.y:.2f}")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/area_model_generator.py
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 | |
generate_area_model
¶
generate_area_model() -> DataFrame
Generate complete area model with node counts and representative points.
Creates a comprehensive area model DataFrame by combining base area discovery, node count aggregation, and representative geographic point calculation. This is the main method for generating complete area models.
The generated model includes
- All unique areas from node data
- Node count per area for capacity/infrastructure analysis
- Representative geographic points for visualization
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Complete area model with node counts and geographic data. Index contains area identifiers, columns include 'node_count' and 'projection_point' (if geographic data available). |
Example:
>>> generator = AreaModelGenerator(node_data, 'country')
>>> area_model = generator.generate_area_model()
>>> print(area_model)
node_count projection_point
country
DE 2 POINT (10.5 52.5)
FR 2 POINT (2.5 48.5)
BE 1 POINT (4 50)
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/area_model_generator.py
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 | |
enhance_with_geometry
¶
enhance_with_geometry(area_model_df: DataFrame, area_gdf: GeoDataFrame) -> GeoDataFrame
Enhance area model with geometric polygon data for spatial analysis.
Integrates area polygon geometries from a GeoDataFrame into the area model, enabling advanced spatial analysis, visualization, and border calculations. The method matches areas by index and creates a proper GeoDataFrame output.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_model_df
|
DataFrame
|
Area model DataFrame to enhance with geometry. |
required |
area_gdf
|
GeoDataFrame
|
GeoDataFrame containing area polygon geometries. Must have 'geometry' column with Polygon or MultiPolygon objects. Areas are matched by index values. |
required |
Returns:
| Type | Description |
|---|---|
GeoDataFrame
|
gpd.GeoDataFrame: Enhanced area model as GeoDataFrame with geometry column. All original data is preserved, geometry column is added for areas that exist in both DataFrames. Missing geometries are set to None. |
Example:
>>> import geopandas as gpd
>>> from shapely.geometry import Polygon
>>>
>>> # Create area geometries
>>> area_polygons = gpd.GeoDataFrame({
>>> 'area_name': ['Germany', 'France', 'Belgium'],
>>> 'geometry': [
>>> Polygon([(9, 51), (12, 51), (12, 54), (9, 54)]),
>>> Polygon([(1, 47), (4, 47), (4, 50), (1, 50)]),
>>> Polygon([(3, 49), (5, 49), (5, 51), (3, 51)])
>>> ]
>>> }, index=['DE', 'FR', 'BE'])
>>>
>>> # Enhance area model with geometry
>>> geo_model = generator.enhance_with_geometry(area_model, area_polygons)
>>> print(f"Model has geometry: {isinstance(geo_model, gpd.GeoDataFrame)}")
>>>
>>> # Use for spatial operations
>>> total_area = geo_model['geometry'].area.sum()
>>> print(f"Total area: {total_area:.0f} square units")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/area_model_generator.py
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 | |
Border model generation for energy system area connectivity analysis.
This module provides functionality for identifying and modeling borders between energy system areas based on line topologies. It supports the creation of comprehensive border_model_dfs that capture directional relationships, naming conventions, and geometric properties essential for energy systems analysis.
Key Capabilities
- Automatic border identification from line topology
- Standardized border naming conventions with directional awareness
- Integration with geometric border calculators
- Network graph generation for area connectivity analysis
- Support for both physical and logical borders (geographically touching borders vs geographically separated borders)
Typical Energy Use Cases
- Modeling interconnections between countries, control areas, or market zones
- Cross-border capacity and flow analysis
- Network visualization and analysis
MESQUAL Integration
This module integrates with MESQUAL's area accounting system to provide border_model_df building capabilities that support spatial energy system analysis and cross-border flow calculations.
AreaBorderNamingConventions
¶
Standardized naming conventions for energy system area borders.
This class provides consistent naming patterns for borders between energy system areas (countries, bidding zones, market regions). It ensures standardized naming across different analysis workflows and supports bidirectional relationship management.
The naming system supports
- Configurable separators and prefixes/suffixes
- Bidirectional border identification (A-B and B-A)
- Alphabetically sorted canonical border names
- Consistent column naming for source and target areas
Key Features
- Configurable naming patterns for different use cases
- Automatic opposite border name generation
- Alphabetical sorting for canonical border representation
- Consistent identifier generation for database/DataFrame columns
Attributes:
| Name | Type | Description |
|---|---|---|
JOIN_AREA_NAMES_BY |
str
|
Separator for area names in border identifiers |
SOURCE_AREA_IDENTIFIER_SUFFIX |
str
|
Suffix for source area column names |
TARGET_AREA_IDENTIFIER_SUFFIX |
str
|
Suffix for target area column names |
OPPOSITE_BORDER_IDENTIFIER |
str
|
Column name for opposite border references |
SORTED_BORDER_IDENTIFIER |
str
|
Column name for alphabetically sorted borders |
NAME_IS_ALPHABETICALLY_SORTED_IDENTIFIER |
str
|
Boolean indicator column |
Example:
>>> conventions = AreaBorderNamingConventions('country')
>>> border_name = conventions.get_area_border_name('DE', 'FR')
>>> print(border_name) # 'DE - FR'
>>> opposite = conventions.get_opposite_area_border_name(border_name)
>>> print(opposite) # 'FR - DE'
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | |
__init__
¶
__init__(area_column: str, border_identifier: str = None, source_area_identifier: str = None, target_area_identifier: str = None)
Initialize border naming conventions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_column
|
str
|
Name of the area column (e.g., 'country', 'bidding_zone') |
required |
border_identifier
|
str
|
Custom name for border identifier column. Defaults to '{area_column}_border' |
None
|
source_area_identifier
|
str
|
Custom name for source area column. Defaults to '{area_column}_from' |
None
|
target_area_identifier
|
str
|
Custom name for target area column. Defaults to '{area_column}_to' |
None
|
Example:
>>> # Standard naming
>>> conventions = AreaBorderNamingConventions('country')
>>> print(conventions.border_identifier) # 'country_border'
>>>
>>> # Custom naming
>>> conventions = AreaBorderNamingConventions(
... 'bidding_zone',
... border_identifier='interconnection',
... source_area_identifier='origin_zone'
... )
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | |
get_area_border_name
¶
get_area_border_name(area_from: str, area_to: str) -> str
Generate standardized border name from source and target areas.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_from
|
str
|
Source area identifier (e.g., 'DE', 'FR_North') |
required |
area_to
|
str
|
Target area identifier (e.g., 'FR', 'DE_South') |
required |
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Formatted border name using the configured separator (e.g. 'DE - FR', 'FR_North - DE_South') |
Example:
>>> conventions = AreaBorderNamingConventions('country')
>>> border_name = conventions.get_area_border_name('DE', 'FR')
>>> print(border_name) # 'DE - FR'
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | |
decompose_area_border_name_to_areas
¶
decompose_area_border_name_to_areas(border_name: str) -> Tuple[str, str]
Extract source and target area names from border identifier.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
border_name
|
str
|
Border name in standard format (e.g., 'DE - FR') |
required |
Returns:
| Type | Description |
|---|---|
Tuple[str, str]
|
Tuple[str, str]: Source and target area names |
Raises:
| Type | Description |
|---|---|
ValueError
|
If border_name doesn't contain the expected separator |
Example:
>>> conventions = AreaBorderNamingConventions('country')
>>> area_from, area_to = conventions.decompose_area_border_name_to_areas('DE - FR')
>>> print(f"From: {area_from}, To: {area_to}") # From: DE, To: FR
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | |
get_opposite_area_border_name
¶
get_opposite_area_border_name(border_name: str) -> str
Generate the opposite direction border name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
border_name
|
str
|
Original border name (e.g., 'DE - FR') |
required |
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Opposite direction border name (e.g., 'FR - DE') |
Example:
>>> conventions = AreaBorderNamingConventions('country')
>>> opposite = conventions.get_opposite_area_border_name('DE - FR')
>>> print(opposite) # 'FR - DE'
Energy Domain Context
Energy flows and capacities are often directional, requiring tracking of both A→B and B→A relationships for comprehensive border analysis.
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 | |
get_alphabetically_sorted_border
¶
get_alphabetically_sorted_border(border_name: str) -> str
Generate alphabetically sorted canonical border name.
Creates a canonical representation where area names are sorted alphabetically, useful for identifying unique borders regardless of direction specification, or for matching borders of opposite direction.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
border_name
|
str
|
Border name in any direction (e.g., 'FR - DE' or 'DE - FR') |
required |
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Alphabetically sorted border name (e.g., 'DE - FR') |
Example:
>>> conventions = AreaBorderNamingConventions('country')
>>> sorted_border = conventions.get_alphabetically_sorted_border('FR - DE')
>>> print(sorted_border) # 'DE - FR'
Use Case
Canonical naming is essential for border deduplication and consistent reference in energy system databases and analysis.
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | |
AreaBorderModelGenerator
¶
Bases: AreaBorderNamingConventions
Generates comprehensive border models from energy system topology.
This class analyzes line connectivity and node-to-area mappings to automatically identify borders between energy system areas. It creates a comprehensive border_model_df with standardized naming, directional relationships, and integration points for geometric analysis.
The generator processes line topology data to identify cross-area connections. It supports bidirectional relationship tracking and provides network graph representations for connectivity analysis.
Key Features
- Automatic border discovery from line topology
- Bidirectional border relationship management
- Standardized naming conventions with configurable patterns
- Network graph generation for connectivity analysis
- Integration with geometric border calculators
- Support for different area granularities (countries, bidding zones, etc.)
MESQUAL Integration
Designed to work with MESQUAL's area accounting system, providing border modeling capabilities that integrate with flow calculators, capacity analyzers, and visualization tools.
Attributes:
| Name | Type | Description |
|---|---|---|
line_model_df |
DataFrame
|
Transmission line data with topology information |
node_model_df |
DataFrame
|
Node data with area assignments |
node_from_col |
str
|
Column name for line source nodes |
node_to_col |
str
|
Column name for line target nodes |
node_to_area_map |
dict
|
Mapping from nodes to their assigned areas |
Example:
>>> # Create border model from transmission data
>>> generator = AreaBorderModelGenerator(
... node_df, line_df, 'country', 'node_from', 'node_to'
... )
>>> border_model = generator.generate_area_border_model()
>>> print(f"Found {len(border_model)} directional borders")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | |
__init__
¶
__init__(node_model_df: DataFrame, line_model_df: DataFrame, area_column: str, node_from_col: str, node_to_col: str, border_identifier: str = None, source_area_identifier: str = None, target_area_identifier: str = None)
Initialize the area border model generator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node_model_df
|
DataFrame
|
DataFrame containing node-level data with area assignments. Must contain area_column with area identifiers for each node. |
required |
line_model_df
|
DataFrame
|
DataFrame containing transmission line topology data. Must contain node_from_col and node_to_col with node identifiers. |
required |
area_column
|
str
|
Column name in node_model_df containing area assignments (e.g., 'country', 'bidding_zone', 'market_region') |
required |
node_from_col
|
str
|
Column name in line_model_df for source node identifiers |
required |
node_to_col
|
str
|
Column name in line_model_df for target node identifiers |
required |
border_identifier
|
str
|
Custom border column name (optional) |
None
|
source_area_identifier
|
str
|
Custom source area column name (optional) |
None
|
target_area_identifier
|
str
|
Custom target area column name (optional) |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If required columns are not found in input DataFrames |
Example:
>>> generator = AreaBorderModelGenerator(
... nodes_df=node_data,
... lines_df=transmission_data,
... area_column='bidding_zone',
... node_from_col='bus_from',
... node_to_col='bus_to'
... )
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | |
generate_area_border_model
¶
generate_area_border_model() -> DataFrame
Generate comprehensive border model with all relationship data.
Analyzes transmission line topology to identify borders between areas, creating a comprehensive DataFrame with directional relationships, naming conventions, and reference data for further analysis.
The generated model includes
- Border identifiers in both directions (A→B and B→A)
- Source and target area columns
- Opposite border references for bidirectional analysis
- Alphabetically sorted canonical border names
- Boolean indicators for alphabetical sorting
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Comprehensive border model indexed by border identifiers. Returns empty DataFrame with proper column structure if no borders found. |
Example:
>>> border_model = generator.generate_area_border_model()
>>> print(border_model.columns)
['country_from', 'country_to', 'opposite_border', 'sorted_border', 'name_is_alphabetically_sorted']
>>>
>>> # Access border relationships
>>> for border_id, row in border_model.iterrows():
... print(f"{border_id}: {row['country_from']} → {row['country_to']}")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | |
get_area_graph
¶
get_area_graph() -> Graph
Generate NetworkX graph representation of area connectivity.
Creates an undirected graph where nodes represent areas and edges represent borders. This is useful for network analysis, path finding, and connectivity studies in multi-area energy systems.
Returns:
| Type | Description |
|---|---|
Graph
|
nx.Graph: Undirected graph with areas as nodes and borders as edges. Graph may contain multiple disconnected components if areas are not fully interconnected. |
Example:
>>> graph = generator.get_area_graph()
>>> print(f"Areas: {list(graph.nodes())}")
>>> print(f"Borders: {list(graph.edges())}")
>>>
>>> # Check connectivity
>>> connected = nx.is_connected(graph)
>>> print(f"All areas connected: {connected}")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 | |
enhance_with_geometry
¶
enhance_with_geometry(border_model_df: DataFrame, area_geometry_calculator: AreaBorderGeometryCalculator) -> DataFrame
Enhance border model with geometric properties for visualization.
Integrates with AreaBorderGeometryCalculator to add geometric information to borders, including representative points, directional angles, and line geometries. This enables advanced visualization of energy system borders.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
border_model_df
|
DataFrame
|
Border model DataFrame to enhance |
required |
area_geometry_calculator
|
AreaBorderGeometryCalculator
|
Configured geometry calculator with area polygon data for geometric computations |
required |
Returns:
| Type | Description |
|---|---|
DataFrame
|
pd.DataFrame: Enhanced border model with additional geometric columns: - projection_point: Point for label/arrow placement - azimuth_angle: Directional angle in degrees - is_physical: Boolean indicating if border is physical (touching areas) - geo_line_string: LineString geometry representing the border |
Example:
>>> # Setup geometry calculator with area polygons
>>> geo_calc = AreaBorderGeometryCalculator(area_polygons_gdf)
>>>
>>> # Enhance border model
>>> enhanced_borders = generator.enhance_with_geometry(border_model, geo_calc)
>>> print(enhanced_borders.columns) # Includes geometric properties
Note
Geometric enhancement may fail for some borders due to missing area geometries or calculation errors. Such failures are logged as warnings without stopping the overall process.
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_generator.py
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | |
AreaBorderGeometryCalculator
¶
Bases: GeoModelGeneratorBase
Advanced geometric calculator for energy system area border analysis.
This class provides sophisticated geometric calculations for borders between energy system areas, handling both physical borders (adjacent areas sharing geographic boundaries) and logical borders (non-adjacent areas requiring connection paths, e.g. through the sea). It's specifically designed to generate properties for energy market cross-border visualizations.
The calculator combines multiple geometric algorithms: - Physical border extraction using geometric intersection - Logical geo-line-border path finding with obstacle avoidance - Representative point computation using pole of inaccessibility for label placements on maps - Azimuth angle calculation for flow icon (arrow) visualization - Geometric validation and optimization
Key Features
- Automatic detection of physical vs logical borders
- Optimal path finding for non-crossing connections
- Representative point calculation for label placement
- Directional angle computation for arrow orientation
- Performance optimization with geometric caching
- Integration with MESQUAL area accounting workflows
Energy Domain Applications
- Visualization of cross-border (cross-country, cross-biddingzone, cross-macroregion) variables (flows, spreads, capacities, ...)
Attributes:
| Name | Type | Description |
|---|---|---|
area_model_gdf |
GeoDataFrame
|
GeoDataFrame with area polygon geometries |
non_crossing_path_finder |
NonCrossingPathFinder
|
Path optimization engine |
_centroid_cache |
dict
|
Cached representative points for performance |
_line_cache |
dict
|
Cached border lines for repeated calculations |
Example:
>>> import geopandas as gpd
>>> from shapely.geometry import box
>>>
>>> # Setup area geometries
>>> areas = gpd.GeoDataFrame({
... 'geometry': [box(0, 0, 1, 1), box(2, 0, 3, 1)] # Two separate areas
... }, index=['Area_A', 'Area_B'])
>>>
>>> # Calculate border geometry
>>> calculator = AreaBorderGeometryCalculator(areas)
>>> border_info = calculator.calculate_border_geometry('Area_A', 'Area_B')
>>> print(f"Border type: {'Physical' if border_info['is_physical'] else 'Logical'}")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 | |
__init__
¶
__init__(area_model_gdf: GeoDataFrame, non_crossing_path_finder: NonCrossingPathFinder = None)
Initialize the border geometry calculator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_model_gdf
|
GeoDataFrame
|
GeoDataFrame containing area geometries with polygon boundaries. Index should contain area identifiers (e.g., country codes, bidding zone names). Must contain valid polygon geometries in 'geometry' column. |
required |
non_crossing_path_finder
|
NonCrossingPathFinder
|
Optional custom path finder for logical borders. If None, creates default NonCrossingPathFinder with standard parameters. |
None
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If geometries are invalid or area_model_gdf lacks required structure |
Example:
>>> areas_gdf = gpd.read_file('countries.geojson').set_index('ISO_A2')
>>> calculator = AreaBorderGeometryCalculator(areas_gdf)
>>>
>>> # Custom path finder for specific requirements
>>> custom_finder = NonCrossingPathFinder(num_points=200, min_clearance=10000)
>>> calculator = AreaBorderGeometryCalculator(areas_gdf, custom_finder)
Note
Invalid geometries are automatically cleaned using buffer(0) operation. Large area datasets benefit from using projected coordinate systems for accurate geometric calculations.
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | |
calculate_border_geometry
¶
calculate_border_geometry(area_from: str, area_to: str) -> dict[str, Union[Point, float, LineString, bool]]
Calculate comprehensive geometric properties for an area border.
This is the main interface method that computes all geometric properties needed for border visualization and analysis. It automatically detects whether areas are physically adjacent or logically connected and applies appropriate geometric algorithms.
Processing Logic
- Detect if areas share physical boundary (touching/intersecting)
- For physical borders: extract shared boundary line
- For logical borders: compute optimal connection path
- Calculate representative point for label/arrow placement
- Compute azimuth angle for arrow icon visualization
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_from
|
str
|
Source area identifier (must exist in area_model_gdf index) |
required |
area_to
|
str
|
Target area identifier (must exist in area_model_gdf index) |
required |
Returns:
| Name | Type | Description |
|---|---|---|
dict |
dict[str, Union[Point, float, LineString, bool]]
|
Comprehensive border geometry information containing: - 'projection_point' (Point): Optimal point for label/arrow placement - 'azimuth_angle' (float): Directional angle in degrees (0-360) - 'geo_line_string' (LineString): Border line geometry - 'is_physical' (bool): True for touching areas, False for logical borders |
Raises:
| Type | Description |
|---|---|
KeyError
|
If area_from or area_to not found in area_model_gdf |
ValueError
|
If geometric calculations fail |
Example:
>>> border_info = calculator.calculate_border_geometry('DE', 'FR')
>>>
>>> # Use for visualization
>>> point = border_info['projection_point']
>>> angle = border_info['azimuth_angle']
>>> is_physical = border_info['is_physical']
>>>
>>> print(f"Border DE→FR: {point} at {angle}° ({'physical' if is_physical else 'logical'})")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | |
areas_touch
¶
areas_touch(area_from: str, area_to: str) -> bool
Check if two areas share a common physical (geographic) border.
Uses Shapely's touches() method to determine if area boundaries intersect without overlapping. This is the standard definition of physical adjacency for energy market regions.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_from
|
str
|
Source area identifier |
required |
area_to
|
str
|
Target area identifier |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if areas share a common boundary, False otherwise |
Example:
>>> touching = calculator.areas_touch('DE', 'FR') # True for neighboring countries
>>> separated = calculator.areas_touch('DE', 'GB') # False for non-adjacent countries
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
areas_intersect
¶
areas_intersect(area_from: str, area_to: str) -> bool
Check if two areas have any geometric intersection.
Uses Shapely's intersects() method to check for any form of geometric intersection, including touching, overlapping, or containment. This is broader than the touches() check and handles edge cases in geographic data.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_from
|
str
|
Source area identifier |
required |
area_to
|
str
|
Target area identifier |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
True if areas have any geometric intersection, False otherwise |
Note
This method is used as a fallback for areas_touch() to handle geographic data with small overlaps or slight topology inconsistencies that are common in real-world boundary datasets.
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | |
get_area_border_midpoint_and_angle
¶
get_area_border_midpoint_and_angle(area_from: str, area_to: str) -> tuple[Point, float]
Calculate representative point and directional angle for border.
Computes the optimal point for placing directional indicators (arrows, labels) and the corresponding angle for proper orientation. The algorithm adapts to both physical and logical borders to ensure optimal placement.
For Physical Borders
- Uses midpoint of shared boundary line
- Angle is perpendicular to boundary, pointing toward target area
For Logical Borders
- Uses midpoint of optimal connection line
- Angle follows connection direction from source to target
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_from
|
str
|
Source area identifier |
required |
area_to
|
str
|
Target area identifier |
required |
Returns:
| Type | Description |
|---|---|
tuple[Point, float]
|
tuple[Point, float]: Representative point and directional angle in degrees. Angle range: 0-360 degrees, where 0° is North, 90° is East. |
Example:
>>> point, angle = calculator.get_area_border_midpoint_and_angle('DE', 'FR')
>>> print(f"Place arrow at {point} oriented at {angle}° for DE→FR flow")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | |
get_area_geometry
¶
get_area_geometry(area: str) -> Union[Polygon, MultiPolygon]
Retrieve and validate geometry for a specified area.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area
|
str
|
Area identifier that must exist in area_model_gdf index |
required |
Returns:
| Type | Description |
|---|---|
Union[Polygon, MultiPolygon]
|
Union[Polygon, MultiPolygon]: Cleaned geometry with buffer(0) applied to ensure validity for geometric operations |
Raises:
| Type | Description |
|---|---|
KeyError
|
If area is not found in area_model_gdf |
Note
The buffer(0) operation ensures geometric validity for complex calculations, which is essential for reliable border analysis.
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
get_straight_line_between_areas
¶
get_straight_line_between_areas(area_from: str, area_to: str) -> LineString
Compute optimal straight-line connection between non-adjacent areas.
Creates a direct line connection between area boundaries, with intelligent path optimization to avoid crossing other areas when possible. This is particularly important for non-physical borders.
Algorithm
- Find representative points for both areas
- Create line connecting area centroids
- Calculate intersection points with area boundaries
- Check for conflicts with other areas
- Apply non-crossing path optimization if needed
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
area_from
|
str
|
Source area identifier |
required |
area_to
|
str
|
Target area identifier |
required |
Returns:
| Name | Type | Description |
|---|---|---|
LineString |
LineString
|
Optimized connection line between area boundaries. Line endpoints touch the area boundaries, not the centroids. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If areas are touching (should use physical border instead) |
Example:
>>> # Connect non-adjacent areas (e.g., Germany to UK)
>>> line = calculator.get_straight_line_between_areas('DE', 'GB')
>>> print(f"Connection length: {line.length:.0f} km")
Performance Note
Results are cached to improve performance for repeated calculations. Path optimization can be computationally intensive for complex geometries.
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 | |
NonCrossingPathFinder
¶
Optimized path finder for non-crossing connections between areas.
This class implements an algorithm for finding the shortest path between two polygon areas while maintaining specified clearance from other areas. It's specifically designed for energy system visualization where geographic border line representations should not misleadingly cross through other market areas.
The algorithm uses a brute-force approach to test multiple potential paths and select the optimal solution.
Key Features
- Configurable boundary point sampling density
- Adjustable minimum clearance distances
- Progress tracking for long-running operations
- Optimization for common geometric scenarios
Performance Characteristics
- Time complexity: O(n² × m) where n=num_points, m=number of other areas
- Memory usage scales with point sampling density
- Results improve with higher point sampling but at computational cost
Attributes:
| Name | Type | Description |
|---|---|---|
num_points |
int
|
Number of boundary points to sample per area |
min_clearance |
float
|
Minimum distance from other areas (in CRS units) |
show_progress |
bool
|
Whether to display progress bars for long operations |
Example:
>>> # High-precision path finding
>>> finder = NonCrossingPathFinder(num_points=500, min_clearance=50000)
>>> path = finder.find_shortest_path(area1_poly, area2_poly, other_areas_gdf)
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 | |
__init__
¶
__init__(num_points: int = 100, min_clearance: float = 50000, show_progress: bool = True)
Initialize the non-crossing path finder.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_points
|
int
|
Number of boundary points to sample per polygon. Higher values improve path quality but increase computation time. Typical range: 50-500 depending on precision requirements. |
100
|
min_clearance
|
float
|
Minimum clearance distance from other areas in coordinate reference system units. For geographic coordinates, this is typically in meters when using projected CRS. |
50000
|
show_progress
|
bool
|
Whether to display progress bars during computation. Useful for long-running operations with high num_points values. |
True
|
Example:
>>> # High-precision finder for detailed analysis
>>> finder = NonCrossingPathFinder(
... num_points=300, # High sampling density
... min_clearance=25000, # 25km minimum clearance
... show_progress=True # Show progress for long operations
... )
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 | |
find_shortest_path
¶
find_shortest_path(polygon1: Polygon, polygon2: Polygon, other_areas: GeoDataFrame, name: str = None) -> Union[LineString, None]
Find shortest non-crossing path between two polygons.
Tests all combinations of boundary points between two polygons to find the shortest connection that maintains minimum clearance from other areas. If the algorithm succeedes and finds a non-crossing LineString, it ensures clean visualization paths for energy market border analysis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
polygon1
|
Polygon
|
Source polygon geometry |
required |
polygon2
|
Polygon
|
Target polygon geometry |
required |
other_areas
|
GeoDataFrame
|
GeoDataFrame of areas to avoid crossing through |
required |
name
|
str
|
Optional name for progress tracking display |
None
|
Returns:
| Type | Description |
|---|---|
Union[LineString, None]
|
LineString or None: Shortest valid path between polygons, or None if no path meets clearance requirements |
Algorithm
- Sample boundary points for both polygons
- Buffer other areas by minimum clearance distance
- Test all point-to-point connections
- Filter out paths that cross buffered areas
- Return shortest valid path
Performance Scaling
- Total paths tested: num_points²
- With default num_points=100: tests 10,000 potential paths
- Computation time scales roughly O(n² × m) where m = number of other areas
Example:
>>> path = finder.find_shortest_path(
... source_area, target_area, obstacles_gdf, "Germany to UK"
... )
>>> if path:
... print(f"Found path with length: {path.length:.0f} meters")
... else:
... print("No valid path found with current clearance settings")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/border_model_geometry_calculator.py
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 | |
GeoModelGeneratorBase
¶
Base class for generating geometric models with representative points.
This class provides common functionality for working with geometric representations of energy system areas, including methods for computing representative points within polygons and multipolygons. It's designed to support energy market analysis where spatial aggregation of nodes into areas is required.
The class supports two methods for computing representative points: - 'polylabel': Uses pole of inaccessibility algorithm for optimal label placement - 'representative_point': Uses Shapely's built-in representative point method
Attributes:
| Name | Type | Description |
|---|---|---|
REPRESENTATIVE_POINT_METHOD |
str
|
Method used for computing representative points ('polylabel' or 'representative_point') |
_polylabel_cache |
dict
|
Cache for expensive polylabel calculations |
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/model_generator_base.py
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | |
get_representative_area_point
¶
get_representative_area_point(geom: Union[Polygon, MultiPolygon]) -> Point
Get a representative point for a polygon or multipolygon geometry.
This method computes a point that is guaranteed to be inside the geometry and is suitable for label placement or other visualization purposes in energy system maps. For MultiPolygons, it operates on the largest constituent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
geom
|
Union[Polygon, MultiPolygon]
|
A Shapely Polygon or MultiPolygon geometry representing an energy system area (e.g., bidding zone, market region) |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Point |
Point
|
A Shapely Point guaranteed to be inside the input geometry, suitable for map labels or representative location analysis |
Raises:
| Type | Description |
|---|---|
ValueError
|
If REPRESENTATIVE_POINT_METHOD is not supported |
Example:
>>> from shapely.geometry import Polygon, Point
>>> generator = GeoModelGeneratorBase()
>>> area_polygon = Polygon([(0, 0), (10, 0), (10, 10), (0, 10)])
>>> rep_point = generator.get_representative_area_point(area_polygon)
>>> print(f"Representative point: {rep_point.x:.2f}, {rep_point.y:.2f}")
Source code in submodules/mesqual/mesqual/energy_data_handling/area_accounting/model_generator_base.py
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | |